Tree Grid

Displays and manipulates hierarchical data with consistent schema formatted as a table and provides a line of advanced features such as sorting, filtering, summaries, editing, column pinning, paging, column moving and hiding.

Demo


Getting started

The tree grid is exported as an NgModule, thus all you need to do in your application is to import the IgxTreeGridModule inside your AppModule:

// app.module.ts

import { IgxTreeGridModule } from 'igniteui-angular';

@NgModule({
    imports: [
        ...
        IgxTreeGridModule,
        ...
    ]
})
export class AppModule {}

Usage

The IgxTreeGridComponent shares a lot of features with the IgxGridComponent, but it also adds the ability to display its data hierarchically. In order to achieve this, the IgxTreeGridComponent provides us with a couple of ways to define the relations among our data objects - by using a child collection for every data object or by using primary and foreign keys for every data object.

Tree cells

Regardless of which option is used for building the tree grid's hierarchy (child collection or primary and foreign keys), the tree grid's rows are constructed of two types of cells:

  • IgxGridCellComponent - Ordinary cell that contains a value.
  • IgxTreeGridCellComponent - Tree cell that contains a value, an expand/collapse indicator and an indentation div element, which is based on the level of the cell's row. The level of a row component can be accessed through the level property of its inner treeRow.
Note

Each row can have only one tree cell, but it can have multiple (or none) ordinary cells.

Initial Expansion Depth

Initially the tree grid will expand all node levels and show them. This behavior can be configured using the expansionDepth property. By default its value is Infinity which means all node levels will be expanded. You may control the initial expansion depth by setting this property to a numeric value. For example 0 will show only root level nodes, 1 will show root level nodes and their child nodes and so on.

Child collection

When we are using the child collection option, every data object contains a child collection, that is populated with items of the same type as the parent data object. This way every record in our tree grid will have a direct reference to any of its children. In this case the data property of our tree grid that contains the original data source will be a hierarchically defined collection.

For this sample, let's use the following collection structure:

// Sample Employee Data

export const EMPLOYEE_DATA = [
    {
        Name: "Johnathan Winchester",
        ID: 1,
        HireDate: new Date(2008, 3, 20),
        Age: 55,
        Employees: [
            {
                Name: "Michael Burke",
                ID: 3,
                HireDate: new Date(2011, 6, 3),
                Age: 43,
                Employees: []
            },
            {
                Name: "Thomas Anderson"
                ID: 2,
                HireDate: new Date(2009, 6, 19),
                Age: 29,
                Employees: []
            },
            ...
        ]
    },
    ...
]

Now let's start by importing our data collection and binding it to the data input of our tree grid.

<!--treeGridSample.component.html-->

<igx-tree-grid #treeGrid [data]="localData">
</igx-tree-grid>

In order for the IgxTreeGridComponent to build the hierarchy, we will have to set its childDataKey property to the name of the child collection that is used in each of our data objects. In our case that will be the Employees collection. In addition, we will disable the automatic column generation and define them manually by matching them to the actual properties of our data objects. (The Employees collection will be automatically used for the hierarchy, so there is no need to include it in the columns' definitions.)

<!--treeGridSample.component.html-->

<igx-tree-grid #treeGrid [data]="localData" childDataKey="Employees"
               [autoGenerate]="false">
    <igx-column field="Name" dataType="string"></igx-column>
    <igx-column field="HireDate" dataType="date"></igx-column>
    <igx-column field="Age" dataType="number"></igx-column>
</igx-tree-grid>

We will now enable the row selection and paging features of the tree grid by using the rowSelectable and the paging properties. We will also enable the summaries feature on the first column and the filtering, sorting, editing, moving and resizing features for each of our columns.

<!--treeGridSample.component.html-->

<igx-tree-grid #treeGrid [data]="localData" childDataKey="Employees"
               [autoGenerate]="false" [rowSelectable]="true" [paging]="true" [allowFiltering]="true">
    <igx-column field="Name" dataType="string" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"
                [hasSummary]="true"></igx-column>
    <igx-column field="HireDate" dataType="date" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"></igx-column>
    <igx-column field="Age" dataType="number" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"></igx-column>
</igx-tree-grid>

Finally, we will enable the toolbar of our tree grid, along with the column hiding, column pinning and exporting features by using the showToolbar, columnHiding, columnPinning, exportExcel and exportCsv properties respectively.

<!--treeGridSample.component.html-->

<igx-tree-grid #treeGrid [data]="localData" childDataKey="Employees"
               [autoGenerate]="false" [rowSelectable]="true" [paging]="true" [allowFiltering]="true"
               [showToolbar]="true" toolbarTitle="Employees" [columnHiding]="true" [columnPinning]="true"
               [exportExcel]="true" [exportCsv]="true" exportExcelText="To Excel" exportCsvText="To CSV">
    <igx-column field="Name" dataType="string" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"></igx-column>
    <igx-column field="HireDate" dataType="date" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"></igx-column>
    <igx-column field="Age" dataType="number" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"></igx-column>
</igx-tree-grid>

You can see the result of the code from above at the beginning of this article in the Demo section.

Primary and Foreign keys

When we are using the primary and foreign keys option, every data object contains a primary key and a foreign key. The primary key is the unique identifier of the current data object and the foreign key is the unique identifier of its parent. In this case the data property of our tree grid that contains the original data source will be a flat collection.

The following is an example of a component which contains a flat collection defined with primary and foreign keys relation:

// treeGridSample.component.ts

@Component({
    ...
})
export class MyComponent implements OnInit {

    public data: any[];

    constructor() { }

    public ngOnInit() {
        // Primary and Foreign keys sample data
        this.data = [
            { ID: 1, ParentID: -1, Name: "Casey Houston", JobTitle: "Vice President", Age: 32 },
            { ID: 2, ParentID: 1, Name: "Gilberto Todd", JobTitle: "Director", Age: 41 },
            { ID: 3, ParentID: 2, Name: "Tanya Bennett", JobTitle: "Director", Age: 29 },
            { ID: 4, ParentID: 2, Name: "Jack Simon", JobTitle: "Software Developer", Age: 33 },
            { ID: 5, ParentID: 8, Name: "Celia Martinez", JobTitle: "Senior Software Developer", Age: 44 },
            { ID: 6, ParentID: -1, Name: "Erma Walsh", JobTitle: "CEO", Age: 52 },
            { ID: 7, ParentID: 2, Name: "Debra Morton", JobTitle: "Associate Software Developer", Age: 35 },
            { ID: 8, ParentID: 10, Name: "Erika Wells", JobTitle: "Software Development Team Lead", Age: 50 },
            { ID: 9, ParentID: 8, Name: "Leslie Hansen", JobTitle: "Associate Software Developer", Age: 28 },
            { ID: 10, ParentID: -1, Name: "Eduardo Ramirez", JobTitle: "Development Manager", Age: 53 }
        ];
    }
}

In the sample data above, all records have an ID, a ParentID and some additional properties like Name, JobTitle and Age. As mentioned previously, the ID of the records must be unique. The ParentID contains the ID of the parent node. If a row has a ParentID that does not match any row in the tree grid, then that means this row is a root row.

The parent-child relation is configured using the tree grid's primaryKey and foreignKey properties.

Here is the template of the component which demonstrates how to configure the tree grid to display the data defined in the above flat collection:

<!--treeGridSample.component.html-->

<igx-tree-grid #treeGrid [data]="data" primaryKey="ID" foreignKey="ParentID"
    [autoGenerate]="false">
    <igx-column field="Name" dataType="string"></igx-column>
    <igx-column field="JobTitle" dataType="string"></igx-column>
    <igx-column field="Age" dataType="number"></igx-column>
</igx-tree-grid>

In addition we will enable the row selection feature of the tree grid by using the rowSelectable property and also the filtering, sorting, editing, moving and resizing features for each of our columns.

<!--treeGridSample.component.html-->

<igx-tree-grid #treeGrid [data]="data" primaryKey="ID" foreignKey="ParentID"
    [autoGenerate]="false" [rowSelectable]="true" [allowFiltering]="true">
    <igx-column field="Name" dataType="string" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"></igx-column>
    <igx-column field="JobTitle" dataType="string" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"></igx-column>
    <igx-column field="Age" dataType="number" [sortable]="true" [editable]="true" [movable]="true" [resizable]="true"></igx-column>
</igx-tree-grid>

And here is the final result:


Row Editing

Row editing - allows modification of several cells in the row, before submitting, at once, all those changes to the tree grid's data source. Leverages the pending changes functionality of the new transaction provider.

If rowEditable is enabled, then all columns that have field property defined (excluding a primaryKey one) will be editable, even though the editable property is not defined for them. If you want to disable editing for a specific column, then you set the editable column's input to false. The following sample illustrates how to enable row editing using the grid's rowEditable property.

Let's start with defining a tree grid with bound data source and rowEditable set to true:

    <igx-tree-grid #treeGrid1 [data]="data" primaryKey="EmployeID" foreignKey="PID" width="100%" height="500px"
        rowEditable=true rowSelectable=true>
        <igx-column *ngFor="let c of columns"
            [editable]="c.editable"
            [field]="c.field"
            [dataType]="c.dataType"
            [header]="c.label"
            [movable]="c.movable"
            [resizable]="c.resizable"
            [sortable]="c.sortable"
            [filterable]="c.filterable"
            >
        </igx-column>
    </igx-tree-grid>
Note

Setting primary key is mandatory for editing operations, including row editing.

Note

It's not needed to enable editing for individual columns. Using the rowEditable property in the grid, will mean that all rows, with defined field property, excluding primary one, will be editable. If you want to disable editing for specific column, then you set the editable column's input to false.

import { Component, OnInit, ViewChild } from "@angular/core";
import { IgxTreeGridComponent } from "igniteui-angular";
...

@Component({
    providers: [],
    selector: "app-tree-grid-row-editing-sample",
    styleUrls: ["tree-grid-row-editing-sample.component.scss"],
    templateUrl: "tree-grid-row-editing-sample.component.html"
})
export class TreeGridRowEditSampleComponent implements OnInit {

    public data: any[];
    public columns: any[];

    @ViewChild("treeGrid1") public treeGrid1: IgxTreeGridComponent;
    public ngOnInit(): void {
        this.data = FLAT_DATA;
        this.columns = [
            { field: "FirstName", label: "First Name", resizable: true, movable: true, sortable: true, filterable: true, editable: true, dataType: "string" },
            { field: "LastName", label: "Last Name", resizable: false, movable: false, sortable: false, filterable: false, editable: true, dataType: "string" },
            { field: "Title", label: "Title", resizable: true, movable: true, sortable: true, filterable: true, editable: true, dataType: "string" },
            { field: "HireDate", label: "Hire Date", resizable: true, movable: true, sortable: true, filterable: true, editable: true, dataType: "date" }
        ];
    }
Note

The grid uses internally a provider IgxBaseTransactionService that holds pending cell changes, until row state submitted or cancelled.

Here is the result:


Batch Editing

HierarchicalTransactionService is an injectable middleware that a component can use to accumulate changes without affecting the underlying data. The provider exposes API to access, manipulate changes (undo and redo) and discard or commit all to the data.

The HierarchicalTransactionService works with both cell editing and row editing. The transaction for the cell edit is added when the cell exits edit mode, while row transaction is created, when the row exits edit mode. In both cases the state of the grid edits consist of all updated, added and deleted rows and their last states. Those can later be inspected, manipulated and submitted at once. Changes are collected for individual cells or rows, depending on the editing mode in use, and accumulated per data row/record.

Batch editing allows to Add/Update/Delete several records in a chunk and manually commit all of the editing changes at ones. Until the changes are committed, there is a visual representation for each edited record so that the end user will be able to differentiate the updated and deleted ones from the unmodified for example. Additionally, it exposes Undo/Redo functionality, that can be used to manage the changes before committing.

In order to be able to use the Batch Editing functionality, it is required to import the HierarchicalTransactionService from "igniteui-angular". Again Transaction is a provider that accumulates the applied changes as a transaction log and in the same time holds a state for each modified row and its last state.

Note

Transaction state consists of all the updated, added and deleted rows and their last states.

To get started import the IgxTreeGridModule in the app.module.ts file:

// app.module.ts

import { IgxTreeGridModule } from 'igniteui-angular';

@NgModule({
    imports: [
        ...
        IgxTreeGridModule,
        ...
    ]
})
export class AppModule {}

Then you need to define the igxTransactionService as provider for the tree grid itself as shown below, OR on some of its parent components:

import { Component, ViewChild } from "@angular/core";
import { IgxGridComponent, IgxGridTransaction, IgxToggleDirective,
    IgxTransactionService, IgxTreeGridComponent } from "igniteui-angular";

@Component({
    providers: [{ provide: IgxGridTransaction, useClass: IgxHierarchicalTransactionService }],
    selector: "app-tree-grid-batch-editing-sample.component",
    styleUrls: ["tree-grid-batch-editing-sample.component.scss"],
    templateUrl: "tree-grid-batch-editing-sample.component.html"
})
export class TreeGridBatchEditingSampleComponent {
Note

IgxGridTransaction is an injection token defined by the grid.

Then define a tree grid with bound data source and bind:

    <igx-tree-grid #treeGrid [data]="data" primaryKey="EmployeID" foreignKey="PID" width ="100%" height ="500px">
        ...
    </igx-tree-grid>
    ...
        <button igxButton (click)="addRow('treeGrid')">Add Row</button>
        <button igxButton [disabled]="!undoEnabled" (click)="undo('treeGrid')">Undo</button>
        <button igxButton [disabled]="!redoEnabled" (click)="redo('treeGrid')">Redo</button>
        <button igxButton [disabled]="!hasTransactions" (click)="openCommitDialog('treeGrid')">Commit</button>
    ...

The following code demonstrates the usage of the HierarchicalTransactionService API - undo, redo, commit.


export class TreeGridBatchEditingSampleComponent {
    @ViewChild("treeGrid", { read: IgxTreeGridComponent }) public treeGrid: IgxTreeGridComponent;
    ...
    public deleteRow(event, gridID, rowID) {
        this.treeGrid.deleteRow(rowID);
    }

    public undo(gridID) {
        this.treeGrid.transactions.undo();
    }

    public redo(gridID) {
        this.treeGrid.transactions.redo();
    }

    public commit() {
        this.treeGrid.transactions.commit(this.data);
    }

    public discard() {
        this.treeGrid.transactions.clear();
    }

    public get undoEnabled(): boolean {
        return this.treeGrid.transactions.canUndo;
    }

    public get redoEnabled(): boolean {
       return this.treeGrid.transactions.canRedo;
    }

Persistence and Integration

The indentation of the tree cells persists across other tree grid features like filtering, sorting and paging.

  • When sorting is applied on a column, the data rows get sorted by levels. This means that the root level rows will be sorted independently from their respective children. Their respective children collections will each be sorted independently as well and so on.
  • The first column (the one that has a visibleIndex of 0) is always the tree column.
  • The column that ends up with a visibleIndex of 0 after operations like column pinning, column hiding and column moving becomes the tree column.
  • Exported Excel worksheets reflect the hierarchy by grouping the records as they are grouped in the tree grid itself. All records expanded states would also be persisted and reflected.
  • When exporting to CSV, levels and expanded states are ignored and all data is exported as flat.

Known Limitations

Limitation Description
Templating Tree Cells When templating a tree cell, content that spans outside the boundaries of the cell will not be shown unless positioned in an overlay.
Search API Search API is currently not supported for the tree grid.
Group By Group By feature is not supported, because it is inherent to the tree grid.

API References

Additional Resources

Our community is active and always welcoming to new ideas.

View page on GitHub