Column Pinning (Frozen) in Angular Grid component

17 Feb 202424 minutes to read

In the Syncfusion Angular Grid component, you have the capability to freeze columns, ensuring they remain visible as you scroll through extensive datasets. This functionality significantly improves user experience by keeping critical information constantly within view, even when navigating through large volumes of data. This means that important columns remain fixed in their positions, making it easier to access and reference key data points while working with the grid.

In the following example, the frozenColumns property is set to 2. This configuration freezes the left two columns of the grid, and they will remain fixed in their positions while the rest of the columns grid can be scrolled horizontally.

import { Component, OnInit, ViewChild } from '@angular/core';
import { GridComponent, } from '@syncfusion/ej2-angular-grids';
import { NumericTextBoxComponent } from '@syncfusion/ej2-angular-inputs';
import { data } from './datasource';

@Component({
    selector: 'app-root',
    template: `<div style="display: flex">
    <label style="padding: 10px 10px 26px 0">
      Change the frozen columns:
    </label>
    <ejs-numerictextbox
      id="frozenColumns"
      #frozenColumns
      min="0"
      max="3"
      [validateDecimalOnType]="true"
      decimals="0"
      format="n"
      value="2"
      width="100px"
    ></ejs-numerictextbox>
    <div> 
      <button style="margin-left:5px" ejs-button (click)="frozenColumnFn()">
        Update
      </button>
    </div>
  </div>
  <ejs-grid #grid style="padding: 5px 5px" [dataSource]='data' height=315 [frozenColumns]='2' [allowSelection]='false'  [enableHover]='false'>
    <e-columns>
      <e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
      <e-column field='CustomerID' headerText='Customer ID' width=100></e-column>
      <e-column field='OrderDate' headerText='Order Date' width=100 format='yMd' textAlign='Right'></e-column>
      <e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
      <e-column field='ShipName' headerText='Ship Name' width=130></e-column>
      <e-column field='ShipAddress' headerText='Ship Address' width=140></e-column>
      <e-column field='ShipCity' headerText='Ship City' width=100></e-column>
      <e-column field='ShipCountry' headerText='Ship Country' width=100></e-column>
      <e-column field='ShipRegion' headerText='Ship Region' width=80></e-column>
      <e-column field='ShipPostalCode' headerText='Ship Postal Code' width=110></e-column>
      <e-column field='Freight' headerText='Freight' width=80></e-column>
    </e-columns>
  </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    @ViewChild('grid')
    public grid?: GridComponent;
    @ViewChild('frozenColumns')
    public frozenColumns?: NumericTextBoxComponent;
  
    ngOnInit(): void {
      this.data = data;
    }
  
    frozenColumnFn() {
      (this.grid as GridComponent).frozenColumns = (this.frozenColumns as NumericTextBoxComponent).value;
    }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule, FreezeService, SelectionService, EditService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { NumericTextBoxAllModule, RatingAllModule } from '@syncfusion/ej2-angular-inputs';
import {  ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { AppComponent } from './app.component';

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        GridModule,
        NumericTextBoxAllModule,
        RatingAllModule,
        ButtonModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent],
    providers: [FreezeService, SelectionService, EditService, ToolbarService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

import 'zone.js';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);

  • Frozen columns should not be set outside the grid view port.
  • Frozen Grid support column virtualization feature, which helps to improve the Grid performance while loading a large dataset.
  • The frozen feature is supported only for the columns that are visible in the current view.
  • You can use both frozenColumns property and frozenRows property in the same application.

Freeze particular columns

The Syncfusion Angular Grid provides a valuable feature that enables you to freeze specific columns, significantly enhancing data visibility and improving your overall user experience. This functionality allows you to select particular columns and freeze them by positioning them at the leftmost side of the grid, ensuring they remain fixed in place while the remaining grid columns can still be scrolled horizontally. While the frozenColumns property freezes columns in the order they are initialized in the grid, you can also use the isFrozen property at the column level to freeze a specific column at any desired index on the left side, offering flexibility in managing which columns are frozen.

To freeze a particular column in the grid, you can utilize the isFrozen property of the grid component as true.

The following example demonstrates how to freeze particular column in grid using isFrozen property. This is achieved by the change event of the DropDownList component. Within the change event, you can modify the isFrozen property of the selected column using the getColumnByField method. Afterward, you can use the refreshColumns method to update the displayed columns based on your interaction.

import { Component, OnInit, ViewChild } from '@angular/core';
import { GridComponent } from '@syncfusion/ej2-angular-grids';
import { ChangeEventArgs } from '@syncfusion/ej2-angular-dropdowns';
import { data } from './datasource';

@Component({
  selector: 'app-root',
  template: `
    <div style="display:flex;">
    <label style="padding: 10px 10px 26px 0">Change the frozen column: </label>
    <ejs-dropdownlist
      style="margin-top:5px"
      id="dropdown"
      #dropdown
      index="0"
      width="120"
      [fields]="field"
      [dataSource]="ddlData"
      (change)="columnChange($event)"
      width="100px"
    ></ejs-dropdownlist>
  </div>  
  <ejs-grid #grid id="grid" style="padding: 5px 5px" [dataSource]='data' height=315 [allowSelection]='false'  [enableHover]='false'>
    <e-columns>
      <e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
      <e-column field='CustomerID' headerText='Customer ID' width=100 [isFrozen]="true"></e-column>
      <e-column field='OrderDate' headerText='Order Date' width=100 format='yMd' textAlign='Right'></e-column>
      <e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
      <e-column field='ShipName' headerText='Ship Name' width=130></e-column>
      <e-column field='ShipCity' headerText='Ship City' width=100></e-column>
      <e-column field='ShipCountry' headerText='Ship Country' width=100></e-column>
      <e-column field='ShipRegion' headerText='Ship Region' width=80></e-column>
      <e-column field='ShipPostalCode' headerText='Ship Postal Code' width=110></e-column>
      <e-column field='Freight' headerText='Freight' width=80></e-column>
    </e-columns>
  </ejs-grid>`
})
export class AppComponent implements OnInit {

  public data?: object[];
  @ViewChild('grid')
  public grid?: GridComponent;
  public field?: object;
  public ddlData?: object[] ;

  ngOnInit(): void {  
    this.data = data;
    this.field= { text: 'text', value: 'value' };
    this.ddlData= [
      { text: 'OrderID', value: 'OrderID' },
      { text: 'CustomerID', value: 'CustomerID' },
      { text: 'OrderDate', value: 'OrderDate' },
      { text: 'EmployeeID', value: 'EmployeeID' },
      { text: 'ShipName', value: 'ShipName' },
      { text: 'ShipCity', value: 'ShipCity' },
      { text: 'ShipCountry', value: 'ShipCountry' },
      { text: 'ShipRegion', value: 'ShipRegion' },
      { text: 'ShipPostalCode', value: 'ShipPostalCode' },
      { text: 'Freight', value: 'Freight' },
    ]
  }

  columnChange(args: ChangeEventArgs) {
    
    const selectedColumn = (this.grid as GridComponent).getColumnByField(args.value as string);

    // Iterate through all columns and unfreeze any previously frozen columns
    ((this.grid as GridComponent).columns as { isFrozen: boolean; }[]).forEach((column) => {
      if (column.isFrozen) {
          column.isFrozen = false;
      }
  });

    // Freeze the newly selected column, if it exists
    if (selectedColumn) {
      selectedColumn.isFrozen = true;
    }

    // Refresh the columns
    (this.grid as GridComponent).refreshColumns();
  }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule, FreezeService, SelectionService, EditService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { DropDownListAllModule } from '@syncfusion/ej2-angular-dropdowns';
import { ButtonModule, CheckBoxModule, RadioButtonModule, SwitchModule } from '@syncfusion/ej2-angular-buttons';
import { AppComponent } from './app.component';

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        GridModule,
        DropDownListAllModule,
        ButtonModule, CheckBoxModule, RadioButtonModule, SwitchModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent],
    providers: [FreezeService, SelectionService, EditService, ToolbarService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

import 'zone.js';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);

Freeze direction

In the Syncfusion Angular Grid, the “freeze direction” feature serves to reposition frozen columns either to the left, right, or in a fixed position, while still allowing the remaining columns to be horizontally movable. This feature is designed to optimize user experience by ensuring that critical information remains visible even during horizontal scrolling. By default, when you set the frozenColumns property of the grid or the isFrozen property of individual columns, it results in freezing those columns on the left side of the grid. This helps in keeping important data readily accessible as you navigate through your dataset.

To achieve this, you can utilize the column.freeze property. This property is used to specify the freeze direction for individual columns. The grid will adjust the column positions based on the column.freeze value.

The types of the column.freeze directions:

  • Left: When you set the column.freeze property to Left, specific columns will be frozen on the left side of the grid. The remaining columns will be movable.

  • Right: When you set the column.freeze property to Right, certain columns will be frozen on the right side of the grid, while the rest of the columns remain movable.

  • Fixed: The Fixed direction locks a column at a fixed position within the grid. This ensures that the column is always visible during horizontal scroll.

In the following example, the ShipCountry column is frozen on the left side, the CustomerID column is frozen on the right side and the Freight column is frozen on the fixed of the content table. Additionally, you can modify the column.freeze property to Left, Right and Fixed based on the selected column by utilizing the change event of the DropDownList component.

import { Component, OnInit, ViewChild } from '@angular/core';
import { DropDownListComponent } from '@syncfusion/ej2-angular-dropdowns';
import { GridComponent, freezeDirection } from '@syncfusion/ej2-angular-grids'
import { data } from './datasource';

@Component({
    selector: 'app-root',
    template: `
    <div style="display: flex">
    <label style="padding: 10px 10px 26px 0"> Change column: </label>
    <ejs-dropdownlist
      id="columnDropDown"
      #columnDropDown
      index="7"
      [dataSource]="columnData"
      [fields]="fields"
      width="100px"
    ></ejs-dropdownlist>
    <label style="padding: 10px 10px 26px 0; margin-left:5px">
      Change freeze direction:
    </label>
    <ejs-dropdownlist
      id="directionDropDown"
      #directionDropDown
      index="0"
      [dataSource]="directionData"
      [fields]="fields"
      width="80px"
    ></ejs-dropdownlist>
      <div>
        <button style="margin-left:5px" ejs-button (click)="freezeDirectionFn()">Update</button>
      </div>
    </div>
    <ejs-grid #grid id='grid' style="padding: 5px 5px" [dataSource]='data' height='315' [enableHover]='false'>
        <e-columns>
          <e-column field='OrderID' headerText='Order ID' width='90' textAlign='Right'></e-column>
          <e-column field='Freight' headerText='Freight' width='90' format='C2' textAlign='Right' freeze='Fixed'></e-column>
          <e-column field='CustomerID' headerText='Customer ID' width='100' freeze='Right'></e-column>
          <e-column field='OrderDate' headerText='Order Date' width='100' format="yMd" textAlign='Right'></e-column>
          <e-column field='ShipName' headerText='Ship Name' width='100'></e-column>
          <e-column field='ShipAddress' headerText='Ship Address' width='120'></e-column>
          <e-column field='ShipCity' headerText='Ship City' width='110'></e-column>
          <e-column field='ShipCountry' headerText='Ship Country' width='100' freeze='Left'></e-column>
        </e-columns>
    </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
    @ViewChild('grid')
    public grid?: GridComponent;
    @ViewChild('columnDropDown')
    public columnDropDown?: DropDownListComponent;
    @ViewChild('directionDropDown')
    public directionDropDown?: DropDownListComponent;
    public fields: object = { text: 'name', value: 'id' };
    public columnData?: object[]
    public directionData?: object[];

    ngOnInit(): void {
      this.data = data;
      this.columnData=[
        { id: 'OrderID', name: 'Order ID' },
        { id: 'Freight', name: 'Freight' },
        { id: 'CustomerID', name: 'Customer ID' },
        { id: 'OrderDate', name: 'Order Date' },
        { id: 'ShipName', name: 'Ship Name' },
        { id: 'ShipAddress', name: 'Ship Address' },
        { id: 'ShipCity', name: 'Ship City' },
        { id: 'ShipCountry', name: 'Ship Country' },
      ];
      this.directionData= [
        { id: 'Left', name: 'Left' },
        { id: 'Right', name: 'Right' },
        { id: 'Fixed', name: 'Fixed' },
      ];
    }

    freezeDirectionFn() {
      (this.grid as GridComponent).getColumnByField((this.columnDropDown as DropDownListComponent).value as string).freeze=(this.directionDropDown as DropDownListComponent).value as freezeDirection
      (this.grid as GridComponent).refreshColumns();
    }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule, FreezeService, SelectionService, EditService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { DropDownListAllModule } from '@syncfusion/ej2-angular-dropdowns';
import { AppComponent } from './app.component';

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        GridModule,
        ButtonModule,
        DropDownListAllModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent],
    providers: [FreezeService, SelectionService, EditService, ToolbarService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

import 'zone.js';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);

Change default frozen line color

You can customize the frozen line borders of frozen columns in the Syncfusion Grid component by applying custom CSS styles to the specific frozen column. This allows you to change the border color of the left frozen columns, right frozen columns, and fixed frozen columns to match your application’s design and theme.

To change default frozen line color, use the following class name and apply the border color based on your requirement.

For left frozen columns:

.e-grid .e-leftfreeze.e-freezeleftborder {
    border-right-color: rgb(198, 30, 204);
}

For right frozen columns:

.e-grid .e-rightfreeze.e-freezerightborder {
    border-left-color: rgb(19, 228, 243);
}

For fixed frozen columns, you need to specify both left and right border as mentioned below

.e-grid .e-fixedfreeze.e-freezeleftborder{
    border-left-color: rgb(9, 209, 9); 
}

.e-grid .e-fixedfreeze.e-freezerightborder{
    border-right-color: rgb(10, 224, 10);
}

The following example demonstrates how to change the default frozen line color using CSS.

import { Component, OnInit } from '@angular/core';
import { data } from './datasource';

@Component({
    selector: 'app-root',
    template: `<ejs-grid [dataSource]='data' height=315 [allowSelection]='false' [enableHover]='false'>
    <e-columns>
      <e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
      <e-column field='CustomerID' headerText='Customer ID' width=100 freeze='Left'></e-column>
      <e-column field='OrderDate' headerText='Order Date' width=100 format='yMd' textAlign='Right'></e-column>
      <e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
      <e-column field='ShipName' headerText='Ship Name' width=130></e-column>
      <e-column field='ShipAddress' headerText='Ship Address' width=140 freeze='Fixed'></e-column>
      <e-column field='ShipCity' headerText='Ship City' width=100></e-column>
      <e-column field='ShipCountry' headerText='Ship Country' width=100 freeze='Right'></e-column>
      <e-column field='ShipRegion' headerText='Ship Region' width=80></e-column>
      <e-column field='ShipPostalCode' headerText='Ship Postal Code' width=110></e-column>
      <e-column field='Freight' headerText='Freight' width=80></e-column>
    </e-columns>
  </ejs-grid>`
})
export class AppComponent implements OnInit {

    public data?: object[];
  
    ngOnInit(): void {
      this.data=data
    }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule, FreezeService, SelectionService, EditService, ToolbarService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';

/**
 * Module
 */
@NgModule({
    imports: [
        BrowserModule,
        GridModule,
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent],
    providers: [FreezeService, SelectionService, EditService, ToolbarService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';

import 'zone.js';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);

Deprecated methods

Previous Current Explanation
getMovableRows() gridInstance.getMovableRows()[0].querySelectorAll(‘.e-unfreeze’) getRows() gridInstance.getRows()[0].querySelectorAll(‘.e-unfreeze’) The previous architecture used separate tables for left, right, and movable contents, returning only movable rows when calling the method, whereas the current architecture combines them into one table, returning all rows and introduces the e-unfreeze class for selecting movable rows
getFrozenRightRows() gridInstance.getFrozenRightRows()[0].querySelectorAll(‘.e-rightfreeze’) getRows() gridInstance.getRows()[0].querySelectorAll(‘.e-rightfreeze’) In the previous architecture, it returned only the table rows from the right freeze table, but in the current architecture, all rows of the entire table are returned, introducing the e-rightfreeze class for selecting right freeze rows.
getMovableRowByIndex()
getFrozenRowByIndex()
getFrozenRightRowByIndex()
getRowByIndex() gridInstance.getRowByIndex(1).querySelectorAll(‘.e-unfreeze’) In the previous architecture, separate methods were used to select rows from different table sections, while in the current architecture, the getMovableRowByIndex(), getFrozenRightRowByIndex(), and getFrozenRowByIndex() methods now return the same table row based on the given index. Additionally, class names for table cells (td’s) have been separated into e-leftfreeze, e-unfreeze, and e-rightfreeze, making it easier to customize cells within a row.
getMovableCellFromIndex()
getFrozenRightCellFromIndex()
getCellFromIndex() gridInstance.getCellFromIndex(1,1) In the previous approach, the getMovableCellFromIndex() method was used to choose a specific cell within the movable table, and the getFrozenRightCellFromIndex() method was utilized to target a particular cell within the right freeze table. However, in the current architecture, you have the flexibility to select a specific cell in either the movable or right freeze table by using both the getFrozenRightCellFromIndex() and getMovableCellFromIndex() methods. This new method simplifies the process of selecting and retrieving specific cells within these tables, offering more versatility and convenience.
getMovableDataRows()
getFrozenRightDataRows()
getFrozenDataRows()
getDataRows() gridInstance.getDataRows()[0].querySelectorAll(‘.e-unfreeze’) In the previous approach, there were separate methods (getMovableDataRows(), getFrozenRightDataRows(), and getFrozenDataRows()) for obtaining viewport data rows from the freeze, movable, and right tables individually. However, in the new approach, these methods have been enhanced to return the entire viewport data rows for all sections together, simplifying data retrieval. You can now extract specific cells within these rows using selectors such as e-leftfreeze for the left freeze, e-unfreeze for the movable, and e-rightfreeze for the right freeze tables, providing greater flexibility in working with the data.
getMovableColumnHeaderByIndex()
getFrozenRightColumnHeaderByIndex()
getFrozenLeftColumnHeaderByIndex()
getColumnHeaderByIndex() gridInstance.getColumnHeaderByIndex(1) In the previous architecture, the methods selected movable, right freeze, and left freeze headers separately. However, in the new approach, when using the getMovableColumnHeaderByIndex(), getFrozenRightColumnHeaderByIndex(), and getFrozenLeftColumnHeaderByIndex() methods, you will still obtain the same results as in the previous architecture.

When a validation message is displayed in the frozen part (Left, Right, Fixed) of the table, scrolling is prevented until the validation message is cleared.

Limitations

While freezing columns in the Syncfusion Angular Grid provides enhanced visibility and scrolling capabilities, there are certain limitations to consider. The following features are not supported when using frozen columns:

  • Detail Template
  • Hierarchy Grid
  • Autofill