How to Fix Columns on Left or Right Side in Angular Grid

Created: 28 Oct 2019

IntegralUI Grid component for Angular comes with a built-in option that allows you to fix columns on either the left or right side of the grid. In this article, you will learn how to change the side at which grid column is fixed during run-time, using a dropdown menu from each column header.

Grid component is part of IntegralUI Web
a suite of UI Components for development of web apps

If you have any questions, don't hesitate to contact us at support@lidorsystems.com

In this demo, there is a grid where each column header has a dropdown button in front of its title. When clicked, a dropdown menu will appear with option stating the side at which the column is fixed. There are three options: None, Left and Right that fixes the column at corresponding grid side. Only non-fixed columns are scrollable.

How to Fix Columns on Left and/or Right Side in Angular

To fix a column at specific grid side is simple. Each column object has a field named 'fixed', which can accept one of these three string values:

  • None - states that column is not fixed
  • Left - column is fixed on left side of the grid
  • Right - column is fixed on right side of the grid
public columns: Array;

constructor(){
    this.columns = [
        { id: 1, headerText: "Order ID", width: 90, fixed: 'left' },
        { id: 2, headerText: "Customer", width: 225, fixed: 'left' },
        { 
            id: 3, 
            contentAlignment: "center", 
            headerText: "Ship Mode", 
            editorType: IntegralUIEditorType.DropList,
            editorSettings: {
                items: this.listItems,
                style: {
                    list: {
                        general: {
                            normal: 'grid-fxcol-editor'
                        }
                    }
                }
            },
            width: 180
        },
        { id: 4, headerText: "Ship Date", contentAlignment: "center", width: 120 },
        { id: 5, headerText: "Quantity", contentAlignment: "right", width: 80 },
        { id: 6, headerText: "Price", contentAlignment: "right" }
    ];
}
                            
<div class="app-block" #application>
    <iui-grid [appRef]="applicationRef" [allowAnimation]="true" [controlStyle]="gridStyle" [columns]="columns" [rows]="rows" [rowHeight]="28" [showFooter]="false" #grid>
        <ng-template let-column [iuiTemplate]="{ type: 'header' }">
            <div>
                <iui-dropdown-button [controlStyle]="dropdownStyle" [placement]="buttonPlacement" [direction]="openDirection" [settings]="column.dropDownSettings" (itemClick)="onDropDownItemClicked($event)" (dropDownOpened)="onDropDownOpened($event, column)"></iui-dropdown-button>
                <span class="grid-fxcol-cell-label">{{column.headerText}}</span>
            </div>
        </ng-template>
        <ng-template let-cell [iuiTemplate]="{ type: 'cell' }">
            <span class="grid-fxcol-cell-label">{{cell.text}}</span>
        </ng-template>
    </iui-grid>
</div>
                            

Note When the value of fixed field is undefined, it's presumed that the column is not fixed. It has the same meaning as when it is set to 'none'.

If the size of all fixed columns exceeds the grid width, it will result in cropping the grid view so that content is show with following priority:

  1. Columns fixed on left side will appear on top
  2. Following are columns fixed on right side
  3. At the end are non-fixed columns

This means that if you have too many columns as fixed, left and right fixed columns will appear above scrolling content. For this reason, it is best to allow fixing columns during run-time, for example using a dropdown menu from each column header.

How to Add a DropDown Menu to Grid Column

Column header in angular grid component is fully customizable, it can contain any custom HTML content. In this case, we will use the IntegralUI DropDownButton component, because it shows a dropdown menu when clicked. You only need to set its appearance via CSS and add some code.

In this example, the column header template has a dropdown button placed before its title. It looks like this:

<div class="app-block" #application>
    <iui-grid [appRef]="applicationRef" [allowAnimation]="true" [controlStyle]="gridStyle" [columns]="columns" [rows]="rows" [rowHeight]="28" [showFooter]="false" #grid>
        <ng-template let-column [iuiTemplate]="{ type: 'header' }">
            <div>
                <iui-dropdown-button></iui-dropdown-button>
                <span class="grid-fxcol-cell-label">{{column.headerText}}</span>
            </div>
        </ng-template>
        <ng-template let-cell [iuiTemplate]="{ type: 'cell' }">
            <span class="grid-fxcol-cell-label">{{cell.text}}</span>
        </ng-template>
    </iui-grid>
</div>
                            

To change the appearance of the dropdown component, at first you need to set the controlStyle property to point to an object that contains the new names for the CSS classes. These classes will override the default look set from built-in CSS styles.

Because angular components are closed by default, to allow style changes in code, you will need to set the ViewEncapsulation attribute of you app component class to None.

Note You can also keep the component status closed for style modifications from outside, by creating a CSS style sheet with your changes and include the path to this file as part of settings in your project angular.json file under 'style' section.

import { Component, enableProdMode, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';

@Component({
    selector: 'iui-app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {

    public dropdownStyle: any = {
        general: {
            normal: 'grid-col-dropdown-normal',
            selected: 'grid-col-dropdown-selected'
        }
    }
}
                            
<div class="app-block" #application>
    <iui-grid [appRef]="applicationRef" [allowAnimation]="true" [controlStyle]="gridStyle" [columns]="columns" [rows]="rows" [rowHeight]="28" [showFooter]="false" #grid>
        <ng-template let-column [iuiTemplate]="{ type: 'header' }">
            <div>
                <iui-dropdown-button [controlStyle]="dropdownStyle" ></iui-dropdown-button>
                <span class="grid-fxcol-cell-label">{{column.headerText}}</span>
            </div>
        </ng-template>
        <ng-template let-cell [iuiTemplate]="{ type: 'cell' }">
            <span class="grid-fxcol-cell-label">{{cell.text}}</span>
        </ng-template>
    </iui-grid>
</div>
                            
/* Column DropDown Button */
.grid-col-dropdown-normal {
    background: transparent;
    border: thin solid transparent;
    cursor: pointer;
    float: left;
    margin: 0 3px 0 0;
    vertical-align: top;
}
.grid-col-dropdown-normal:hover, .grid-col-dropdown-selected {
    animation-name: grid-fxcol-dropdown-button-animate;
    animation-delay: 0s;
    animation-direction: normal;
    animation-duration: 0.5s;
    animation-fill-mode: forwards;
    animation-iteration-count: 1;
    animation-play-state: running;
    animation-timing-function: linear; 
}
@keyframes grid-fxcol-dropdown-button-animate
{
    0% { background: transparent; border-color: transparent; }
    100% { background: #f9f9f9; border-color: #afafaf; }
}

.grid-col-dropdown-normal .iui-dropdown-button-btn-left, .grid-col-dropdown-normal .iui-dropdown-button-btn-left span {
    border-right: 0;
    margin-right: 0;
}

.grid-col-dropdown-normal .iui-dropdown-button-btn span {
    background-position: -176px -80px;
}

/* DropDown Menu Icon */
.grid-fxcol-col-item {
    background: url(src/app/resources/icons.png) no-repeat 0 0;
    display: inline-block;
    overflow: hidden;
    padding: 0;
    margin: 0 0 0 5px;
    width: 16px;
    height: 16px;
    vertical-align: middle;
}
.grid-fxcol-col-item-selected {
    background-position: -160px 0;
}
                            

Now you can create a dropdown menu and apply it to each grid column. In order for the menu to appear on top of other HTML elements, it needs a reference to the application top component. To get this reference, set up a variable name to the top element or component in your HTML and then use the angular ViewChild decorator.

Note During initialization this reference variable is empty, because the DOM is not yet created. For this purpose, apply this reference to the dropdown menu settings in the ngAfterViewInit method.

By handling the ngAfterViewInit method, you can cycle through each column object and create the settings for the dropdown button. Each menu will have the same options, only difference is which option is currently selected. This corresponds with the side on which column is fixed.

@ViewChild('application', {read: ViewContainerRef, static: false}) applicationRef: ViewContainerRef;

ngAfterViewInit(){
    // Add DropDownButton with items to each column
    this.columns.map(column => {
        column.dropDownSettings = {
            adjustment: { top: 1, left: 0 },
            appRef: this.applicationRef,
            items: []
        }

        // Create items based on column fixed side and set which item is selected
        for (let i = 0; i < 3; i++){
            let selIndex = this.getSelectedItemIndex(column.fixed);
            let item = {
                key: column.id,
                icon: selIndex == i ? 'grid-fxcol-col-item grid-fxcol-col-item-selected' : 'grid-fxcol-col-item',
                text: i == 0 ? 'None' : (i == 1 ? 'Left' : 'Right'),
                selected: selIndex == i ? true : false
            }

            column.dropDownSettings.items.push(item);
        }
    });


    this.grid.selectedColumn = this.columns[0];
}

private getSelectedItemIndex(fixed: string){
    switch (fixed){
        case 'left':
            return 1;
        case 'right':
            return 2;
    }

    return 0;
}
                            
<div class="app-block" #application>
    <iui-grid [appRef]="applicationRef" [allowAnimation]="true" [controlStyle]="gridStyle" [columns]="columns" [rows]="rows" [rowHeight]="28" [showFooter]="false" #grid>
        <ng-template let-column [iuiTemplate]="{ type: 'header' }">
            <div>
                <iui-dropdown-button [controlStyle]="dropdownStyle" [placement]="buttonPlacement" [direction]="openDirection" [settings]="column.dropDownSettings" ></iui-dropdown-button>
                <span class="grid-fxcol-cell-label">{{column.headerText}}</span>
            </div>
        </ng-template>
        <ng-template let-cell [iuiTemplate]="{ type: 'cell' }">
            <span class="grid-fxcol-cell-label">{{cell.text}}</span>
        </ng-template>
    </iui-grid>
</div>
                            

Finally, you will need to handle the menu events: when it opens and when item is clicked:

  • dropDownOpened - fired when dropdown menu appears
  • itemClick - fired when menu item is clicked

By handling the first event, you can select the column to which the dropdown button belongs. The second event is used to handle changes to the grid when an option is selected. In this case, you need to change the column fixed field based on current selection and then update the grid layout. You also need to update the icon in dropdown menu to point to selected option.

@ViewChild('grid', { static: false }) grid: IntegralUIGrid;


onDropDownOpened(e: any, column: any){
    this.grid.selectedColumn = column;
}

onDropDownItemClicked(e: any){
    // Use the column id stored in the item key to find the related column object
    let column = this.grid.getColumnById(e.item.key);
    if (column){
        column.fixed = e.item.text.toLowerCase();

        // Update the item icon based on column fixed value
        this.updateItems(column.dropDownSettings.items, e.item);

        this.grid.updateLayout();
    }
}

private updateItems(list: Array, selItem: any){
    list.map(item => {
        item.selected = item == selItem;
        item.icon = item.selected ? 'grid-fxcol-col-item grid-fxcol-col-item-selected' : 'grid-fxcol-col-item';
    });
}
                            
<div class="app-block" #application>
    <iui-grid [appRef]="applicationRef" [allowAnimation]="true" [controlStyle]="gridStyle" [columns]="columns" [rows]="rows" [rowHeight]="28" [showFooter]="false" #grid>
        <ng-template let-column [iuiTemplate]="{ type: 'header' }">
            <div>
                <iui-dropdown-button [controlStyle]="dropdownStyle" [placement]="buttonPlacement" [direction]="openDirection" [settings]="column.dropDownSettings" (itemClick)="onDropDownItemClicked($event)" (dropDownOpened)="onDropDownOpened($event, column)"></iui-dropdown-button>
                <span class="grid-fxcol-cell-label">{{column.headerText}}</span>
            </div>
        </ng-template>
        <ng-template let-cell [iuiTemplate]="{ type: 'cell' }">
            <span class="grid-fxcol-cell-label">{{cell.text}}</span>
        </ng-template>
    </iui-grid>
</div>
                            

Finally, you will need to handle the menu events: when it opens and when item is clicked:

  • dropDownOpened - fired when dropdown menu appears
  • itemClick - fired when menu item is clicked

As a result, you can now dynamically change the side at which column is fixed.

This is only an example, you can change how or when columns are fixed in different ways: during data load or using a different UI (it doesn't have to be a dropdown menu). It is totally up to you to decide how you will handle this. In general, only requirement is to set the fixed field in column object to a value that corresponds to the grid side and then just update the grid layout.

Conclusion

When you have a grid with many columns, for quick view at the data you may need to fix some of these columns on left or right side. The IntegralUI Grid is a component for Angular that comes with a built-in option that allows you to fix columns during run-time.

As this example shows, to fix columns dynamically you can create your own custom UI (or use other Angular components) and merge it with the Grid. You can add a dropdown menu with options for fixing columns that will appear whenever a button is clicked from the column header or handle it in a different way. The Grid component allows you to create your own custom solution that is best for your application.

The Grid component is part of IntegralUI Web.

Newsletter


Sign-up to our newsletter and you will receive news on upcoming events, latest articles, samples and special offers.
Name: Email: *
*By checking this box, I agree to receive a newsletter from Lidor Systems in accordance with the Privacy Policy. I understand that I can unsubscribe from these communications at any time by clicking on the unsubscribe link in all emails.