Column pinning (Frozen) in Angular TreeGrid component

10 Sep 202524 minutes to read

In the Syncfusion® Angular TreeGrid component, columns can be frozen, ensuring they remain visible while scrolling through extensive datasets. This functionality significantly improves experience by keeping critical information constantly within view, even when navigating through large volumes of data. Important columns remain fixed in their positions, making it easier to access and reference key data points while working with the TreeGrid.

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

import { NgModule, ViewChild } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid'
import { NumericTextBoxModule } from '@syncfusion/ej2-angular-inputs'
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FreezeService, TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { NumericTextBoxComponent } from '@syncfusion/ej2-angular-inputs';
import { sampleData } from './datasource';

@Component({
    imports: [TreeGridModule, NumericTextBoxModule, ButtonModule],
    standalone: true,
    selector: 'app-container',
    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="5" [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-treegrid #treegrid [dataSource]='data' childMapping='subtasks' [treeColumnIndex]='1' height='310' [frozenColumns]='2' [frozenRows]='3' [allowSelection]='false'>
                  <e-columns>
                    <e-column field='taskID' headerText='Task ID' width='90' textAlign='Right'></e-column>
                    <e-column field='taskName' headerText='Task Name' width='230'></e-column>
                    <e-column field='startDate' headerText='Start Date' width='120' format='yMd' textAlign='Right'></e-column>
                    <e-column field='endDate' headerText='End Date' width='120' format='yMd' textAlign='Right'></e-column>
                    <e-column field='duration' headerText='Duration' width='110' textAlign='Right'></e-column>
                    <e-column field='progress' headerText='Progress' width='110' textAlign='Right'></e-column>
                    <e-column field='priority' headerText='Priority' width='110'></e-column>
                    <e-column field='approved' headerText='Approved' textAlign='Left' width='110'></e-column>
                  </e-columns>
                </ejs-treegrid>`,
    providers: [FreezeService]
})
export class AppComponent implements OnInit {

    public data?: Object[];

    @ViewChild('treegrid')
    public treegrid?: TreeGridComponent;

    @ViewChild('frozenColumns')
    public frozenColumns?: NumericTextBoxComponent;

    ngOnInit(): void {
        this.data = sampleData;
    }
    frozenColumnFn() {
        (this.treegrid as TreeGridComponent).frozenColumns = (this.frozenColumns as NumericTextBoxComponent).value;
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

  • Frozen columns should not be set outside the TreeGrid viewport.
  • Frozen TreeGrid supports column virtualization feature, which helps to improve the TreeGrid performance while loading a large dataset.
  • The frozen feature is supported only for the columns that are visible in the current view.
  • Both frozenColumns property and frozenRows property can be used in the same application.

Freeze particular columns

The Syncfusion® Angular TreeGrid provides a feature that enables freezing specific columns, significantly enhancing data visibility and improving the experience. This functionality allows selecting particular columns and freezing them by positioning them at the leftmost side of the TreeGrid, ensuring they remain fixed in place while the remaining TreeGrid columns can still be scrolled horizontally. While the frozenColumns property freezes columns in the order they are initialized in the TreeGrid, the isFrozen property can also be used 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 TreeGrid, the isFrozen property of the TreeGrid component can be set to true.

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

import { NgModule, ViewChild } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { TreeGridModule, ToolbarService, SelectionService, EditService } from '@syncfusion/ej2-angular-treegrid'
import { DropDownListAllModule } from '@syncfusion/ej2-angular-dropdowns'
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FreezeService, TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { sampleData } from './datasource';
import { ChangeEventArgs } from '@syncfusion/ej2-angular-dropdowns';

@Component({
    imports: [TreeGridModule, DropDownListAllModule],

    providers: [ToolbarService, FreezeService, SelectionService, EditService],
    standalone: true,
    selector: 'app-container',
    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-treegrid #treegrid [dataSource]='data' childMapping='subtasks' height='310' [allowSelection]='false'>
                 <e-columns>
                    <e-column field='taskID' headerText='Task ID' width='90' textAlign='Right'></e-column>
                    <e-column field='taskName' headerText='Task Name' width='200' isFrozen= 'true'></e-column>
                    <e-column field='startDate' headerText='Start Date' isFrozen= 'true' width='150' format='yMd' textAlign='Right'></e-column>
                    <e-column field='endDate' headerText='End Date' width='150' format='yMd' textAlign='Right'></e-column>
                    <e-column field='duration' headerText='Duration' width='110' textAlign='Right'></e-column>
                    <e-column field='progress' headerText='Progress' width='110' textAlign='Right'></e-column>
                    <e-column field='priority' headerText='Priority' width='110'></e-column>
                    <e-column field='approved' headerText='Approved' textAlign='Left' width='110'></e-column>
                  </e-columns>
               </ejs-treegrid>`,
})
export class AppComponent implements OnInit {

    public data?: Object[];
    @ViewChild('treegrid')
    public treegrid?: TreeGridComponent;
    public field?: object;
    public ddlData?: object[];

    ngOnInit(): void {
        this.data = sampleData;
        this.field = { text: 'text', value: 'value' };
        this.ddlData = [
            { text: 'taskID', value: 'taskID' },
            { text: 'taskName', value: 'taskName' },
            { text: 'startDate', value: 'startDate' },
            { text: 'endDate', value: 'endDate' },
            { text: 'duration', value: 'duration' },
            { text: 'progress', value: 'progress' },
            { text: 'priority', value: 'priority' },
            { text: 'approved', value: 'approved' },
        ];
    }
    columnChange(args: ChangeEventArgs) {
        const selectedColumn = (
            this.treegrid as TreeGridComponent
        ).getColumnByField(args.value as string);

        // Iterate through all columns and unfreeze any previously frozen columns
        (
            (this.treegrid as TreeGridComponent).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.treegrid as TreeGridComponent).refreshColumns();
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Freeze direction

In the Syncfusion® Angular TreeGrid, 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 the experience by ensuring that critical information remains visible even during horizontal scrolling. By default, when the frozenColumns property of the TreeGrid or the isFrozen property of individual columns is set, it results in freezing those columns on the left side of the TreeGrid. This helps in keeping important data readily accessible while navigating through the dataset.

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

The types of the column.freeze directions:

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

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

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

In the following example, the taskName column is frozen on the left side, the progress column is frozen on the right side and the priority column is frozen on the fixed of the content table. Additionally, the column.freeze property can be modified to Left, Right and Fixed based on the selected column by utilizing the change event of the DropDownList component.

import { NgModule, ViewChild } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { TreeGridModule, ToolbarService, SelectionService, EditService } from '@syncfusion/ej2-angular-treegrid'
import { DropDownListAllModule } from '@syncfusion/ej2-angular-dropdowns'
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FreezeService, TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
import { sampleData } from './datasource';
import { freezeDirection } from '@syncfusion/ej2-angular-grids';
import {
    ChangeEventArgs,
    DropDownListComponent,
} from '@syncfusion/ej2-angular-dropdowns';

@Component({
    imports: [TreeGridModule, DropDownListAllModule, ButtonModule],
    providers: [ToolbarService, SelectionService, FreezeService, EditService],
    standalone: true,
    selector: 'app-container',
    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]="fields1"
                    width="80px"></ejs-dropdownlist>
                  <div>
                    <button style="margin-left:5px" ejs-button (click)="freezeDirectionFn()">Update</button>
                  </div>
               </div>

                   <ejs-treegrid #treegrid [dataSource]='data' childMapping='subtasks' height='230' [treeColumnIndex]='1' [allowSelection]='false'>
                      <e-columns>
                        <e-column field='taskID' headerText='Task ID' width='90' textAlign='Right'></e-column>
                        <e-column field='taskName' headerText='Task Name' width='200' freeze='Left'></e-column>
                        <e-column field='startDate' headerText='Start Date' width='150' format='yMd' textAlign='Right'></e-column>
                        <e-column field='endDate' headerText='End Date' width='150' format='yMd' textAlign='Right'></e-column>
                        <e-column field='duration' headerText='Duration' width='110' textAlign='Right'></e-column>
                        <e-column field='progress' headerText='Progress' width='110' freeze='Right' textAlign='Right'></e-column>
                        <e-column field='priority' headerText='Priority' freeze='Fixed' width='110'  ></e-column>
                        <e-column field='approved' headerText='Approved' textAlign='Left' width='110'></e-column>
                      </e-columns>
                    </ejs-treegrid>`,
})
export class AppComponent implements OnInit {

    public data?: Object[];
    @ViewChild('treegrid')
    public treegrid?: TreeGridComponent;

    @ViewChild('columnDropDown')
    public columnDropDown?: DropDownListComponent;

    @ViewChild('directionDropDown')
    public directionDropDown?: DropDownListComponent;

    public fields: object = { text: 'text', value: 'value' };
    public fields1: object = { text: 'name', value: 'id' };
    public columnData?: object[];
    public directionData?: object[];

    ngOnInit(): void {
        this.data = sampleData;
        this.directionData = [
            { id: 'Left', name: 'Left' },
            { id: 'Right', name: 'Right' },
            { id: 'Fixed', name: 'Fixed' },
        ];
        this.columnData = [
            { text: 'taskID', value: 'taskID' },
            { text: 'taskName', value: 'taskName' },
            { text: 'startDate', value: 'startDate' },
            { text: 'endDate', value: 'endDate' },
            { text: 'duration', value: 'duration' },
            { text: 'progress', value: 'progress' },
            { text: 'priority', value: 'priority' },
            { text: 'approved', value: 'approved' },
        ];
    }

    freezeDirectionFn() {
        (this.treegrid as TreeGridComponent).getColumnByField(
            (this.columnDropDown as DropDownListComponent).value as string
        ).freeze = (this.directionDropDown as DropDownListComponent)
            .value as freezeDirection;
        (this.treegrid as TreeGridComponent).refreshColumns();
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

Change default frozen line color

The frozen line borders of frozen columns in the Syncfusion® TreeGrid component can be customized by applying custom CSS styles to the specific frozen column. This allows changing the border color of the left frozen columns, right frozen columns, and fixed frozen columns to match the application’s design and theme.

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

For left frozen columns:

.e-treegrid .e-leftfreeze.e-freezeleftborder {
    border-right-color: rgb(0, 255, 0);
}

For right frozen columns:

.e-treegrid .e-rightfreeze.e-freezerightborder {
    border-left-color: rgb(0, 0, 255) !important;
}

For fixed frozen columns, both left and right borders need to be specified as mentioned below:

.e-treegrid .e-leftfreeze.e-freezeleftborder {
    border-right-color: rgb(0, 255, 0);
}

.e-treegrid .e-rightfreeze.e-freezerightborder {
    border-left-color: rgb(0, 0, 255) !important;
}

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

import { NgModule, ViewChild, ViewEncapsulation } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { TreeGridModule, ToolbarService, SelectionService, EditService } from '@syncfusion/ej2-angular-treegrid'
import { TreeGrid, Selection, Edit, Toolbar, EditSettingsModel, FreezeService } from '@syncfusion/ej2-angular-treegrid';
import { Component, OnInit } from '@angular/core';
import { sampleData } from './datasource';

@Component({
    imports: [TreeGridModule],
    encapsulation:ViewEncapsulation.None,
    providers: [ToolbarService, SelectionService, EditService, FreezeService],
    standalone: true,
    selector: 'app-container',
    template: `<ejs-treegrid [dataSource]='data' childMapping='subtasks' height='310' [treeColumnIndex]='0' enableHover='false'>
                 <e-columns>
                    <e-column field='taskID' headerText='Task ID' width='90' textAlign='Right'></e-column>
                    <e-column field='taskName' headerText='Task Name' width='200' freeze='Left'></e-column>
                    <e-column field='duration' headerText='Duration' width='110' textAlign='Right'></e-column>
                    <e-column field='startDate' headerText='Start Date' width='150' format='yMd' textAlign='Right'></e-column>
                    <e-column field='endDate' headerText='End Date' width='150' format='yMd' textAlign='Right'></e-column>
                    <e-column field='progress' headerText='Progress' width='110' textAlign='Right'></e-column>
                    <e-column field='priority' headerText='Priority' width='110' freeze='Right'></e-column>
                    <e-column field='approved' headerText='Approved' textAlign='Left' width='110'></e-column>
                 </e-columns>
                </ejs-treegrid>`,
    styles:[`
    .e-treegrid .e-leftfreeze.e-freezeleftborder {
        border-right-color: rgb(0, 255, 0);
    }
    
    .e-treegrid .e-rightfreeze.e-freezerightborder {
        border-left-color: rgb(0, 0, 255) !important;
    }`]

})
export class AppComponent implements OnInit {

    public data?: object[];

    ngOnInit(): void {
        this.data = sampleData;
    }
}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));

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 TreeGrid provides enhanced visibility and scrolling capabilities, there are certain limitations to consider. The following features are not supported when using frozen rows and columns:

  • Detail Template
  • Row Template
  • Cell Editing