How can I help you?
Hierarchy Grid in Angular Grid Component
19 Mar 202624 minutes to read
The Hierarchy Grid in the Syncfusion® Angular Grid is designed to display hierarchical or nested data structures within a tabular layout. It enables the representation of parent-child relationships by allowing rows to be expanded or collapsed, revealing related child records beneath their corresponding parent rows.
This structure enhances data readability and navigation, especially when working with datasets that include multiple levels of related information. Each parent row can be expanded to display its associated child grid, which can itself be configured with columns, templates, and features similar to the main grid.
To enable the Hierarchy Grid feature:
-
Inject the
DetailRowServicein the providers section. This service is essential for handling the hierarchy grid functionality. -
Define the childGrid property in the grid configuration. This property contains the settings for the child grid, such as its columns and data source.
-
Set the childGrid.queryString property to link the parent and child records using a common field. This defines the retrieval of child data based on the parent row.
The following example demonstrates enabling the hierarchy feature in the grid, which helps present structured data in a clean and interactive format for improved organization and navigation.
import { data, employeeData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { DetailRowService, FilterService, GridModel, GridModule, GroupService, PageService, SortService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [ GridModule ],
providers: [PageService, SortService, FilterService, GroupService, DetailRowService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid [dataSource]='parentData' height='315px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=120></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public childGrid: GridModel = {
dataSource: data,
queryString: 'EmployeeID',
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
};
ngOnInit(): void {
this.parentData = employeeData;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));
- The Grid supports n level of child grids.
- Hierarchical binding is not supported when DetailTemplate is enabled.
Mapping parent-child Grids with different field names
By default, the Grid uses the same field name in both the parent and child grids to establish a hierarchical relationship through the queryString property. However, it also supports scenarios where the parent and child data sources use different key fields.
When the parent and child data sources use different key fields, this relationship can still be configured by handling the child grid’s load event. In this event, the required value (e.g., Employee ID) can be retrieved from parentDetails.parentRowData and dynamically assigned to the appropriate field in the child grid’s query. This approach enables flexible hierarchical binding even when the key fields differ between parent and child grids.
In the following example, the load event is used to customize the mapping value for the child grid. The parentDetails property provides access to the parent row’s data, including the parentKeyFieldValue, which can be used to set the appropriate mapping field. By referencing the “Employee ID” field from the parentRowData, the corresponding value is extracted and applied to construct the query for the child grid.
import { data, employeeData } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { DetailRowService, FilterService, GridComponent, GridModel, GridModule, GroupService, PageService, ParentDetails, SortService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [GridModule],
providers: [PageService, SortService, FilterService, GroupService,DetailRowService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='parentData' height='265px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
@ViewChild('grid')
public grid?: GridComponent;
public parentData?: object[];
public childGrid: GridModel | GridComponent = {
queryString: 'ID',
dataSource: data,
columns: [
{ field: 'OrderID', headerText: 'Order ID', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
load() {
(this.parentDetails as ParentDetails).parentKeyFieldValue = (<{ EmployeeID?: string}>(this.parentDetails as ParentDetails).parentRowData)['EmployeeID'];
}
};
ngOnInit(): void {
this.parentData = employeeData;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Make sure to adjust the field name according to the specific scenario.
Expand child grid initially
Child rows in a hierarchical grid can be expanded automatically during the initial load by calling the expand method within the grid’s dataBound event. This ensures that nested data becomes visible when the grid is rendered, without requiring manual interaction.
In the provided example, the third record of the grid is expanded by utilizing the expand method within the dataBound event.
import { data, employeeData } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { DetailRowService, FilterService, GridComponent, GridModel, GridModule, GroupService, PageService, SortService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [ GridModule ],
providers: [PageService, SortService, FilterService, GroupService, DetailRowService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='parentData' height='265px' [childGrid]='childGrid' (dataBound)='onDataBound()'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=100></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public childGrid: GridModel = {
dataSource: data,
queryString: 'EmployeeID',
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
};
@ViewChild('grid')
public grid?: GridComponent;
ngOnInit(): void {
this.parentData = employeeData;
}
onDataBound(): void {
(this.grid as GridComponent).detailRowModule.expand(2);
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Index values begin with “0”, allowing provision of the desired target index to expand a specific child grid initially.
Dynamically load child grid data
Dynamically loading child grid data improves performance, optimizes data transmission, and enhances the experience by providing on-demand access to relevant information. Additionally, it offers flexibility in data presentation, improving overall application efficiency.
To achieve this, use the load event of the parent grid. This event allows assigning the dataSource for the child grid dynamically based on the parent row’s data. The following example demonstrates dynamically loading child grid data using the load event.
import { data, employeeData } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { DetailRowService, FilterService, GridComponent, GridModel, GridModule, GroupService, PageService, SortService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [ GridModule ],
providers: [PageService, SortService, FilterService, GroupService, DetailRowService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='parentData' height='265px' [childGrid]='childGrid' (load)='onLoad()'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=100></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public childGrid: GridModel = {
queryString: 'EmployeeID',
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
};
@ViewChild('grid')
public grid?: GridComponent;
ngOnInit(): void {
this.parentData = employeeData;
}
onLoad(): void {
(this.grid as GridComponent).childGrid.dataSource = data; // assign data source for child grid.
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Dynamic data binding in Child Grids using parent row values
A child grid can be bound dynamically to display data that corresponds to its parent row. Instead of relying on the queryString property, this can be handled through the detailDataBound event, which is triggered whenever a child grid expands.
Within the detailDataBound event, the child grid’s dataSource is filtered based on the parent row’s “Employee ID”. The DataManager is used to apply the filter, and the filtered result is then assigned to the child grid’s dataSource. This ensures that each child grid shows only the records related to its parent, creating a contextual and flexible hierarchical display.
import { childColumnDataType, childData, employeeData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { DetailDataBoundEventArgs, DetailRowService, FilterService, GridModel, GridModule, GroupService, IGrid, PageService, SortService } from '@syncfusion/ej2-angular-grids';
import { DataManager, Query } from '@syncfusion/ej2-data';
@Component({
imports: [GridModule ],
providers: [PageService, SortService, FilterService,GroupService, DetailRowService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid [dataSource]='parentData' height='265px' [childGrid]='childGrid' (detailDataBound)='detailDataBound($event)'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=100></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public childGrid: GridModel = {
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
};
ngOnInit(): void {
this.parentData = employeeData;
}
detailDataBound({data, childGrid} : DetailDataBoundEventArgs) {
var empIdValue = (data as childColumnDataType).EmployeeID;
var childGridData = new DataManager(childData).executeLocal(
new Query().where('EmployeeID', 'equal', empIdValue, true)
);
(childGrid as IGrid).query = new Query();
(childGrid as IGrid).dataSource = childGridData;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Adding record in child grid
Adding a record to a child grid allows additional data to be maintained for the corresponding parent row. To preserve the parent–child relationship, the appropriate queryString value must be included in the new record.
This is accomplished through the grid’s actionBegin event. In the example, the parent and child grids are related by “Employee ID”, so the child record’s “Employee ID” is assigned the parent row’s queryString value using the actionBegin event to ensure relational consistency.
import { childColumnDataType, data, employeeData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { AddEventArgs, DetailRowService, EditService, FilterService, GridModel, GridModule, GroupService, PageService, ParentDetails, SortService, ToolbarService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [GridModule],
providers: [PageService, SortService, FilterService, GroupService,DetailRowService, EditService, ToolbarService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid [dataSource]='parentData' height='265px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=100></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public childGrid: GridModel = {
dataSource: data,
queryString: 'EmployeeID',
toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel'],
editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true },
columns: [
{ field: 'OrderID', headerText: 'Order ID', isPrimaryKey: true, textAlign: 'Right', width: 90 },
{ field: 'EmployeeID', headerText: 'Employee ID', textAlign: 'Right', allowEditing: false, width: 80 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
actionBegin({ requestType, data }: AddEventArgs) {
if (requestType === 'add') {
// `parentKeyFieldValue` refers to the queryString field value of the parent record.
const parentFieldValue = (this.parentDetails as ParentDetails)?.parentKeyFieldValue;
if (typeof parentFieldValue === 'number') {
(data as childColumnDataType).EmployeeID = parentFieldValue;
}
}
}
};
ngOnInit(): void {
this.parentData = employeeData;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Template column in child grid
A template column in a child grid within the Data Grid component is valuable when customizing the appearance and functionality of specific columns in the child grid. It is useful for incorporating interactive elements, custom formatting, or complex data representation within specific columns of the child grid.
To achieve this, utilize the template property of a column to display a custom element instead of a field value in the grid. Template columns defined in the child grid will be null in the ngOnInit method, which means they will not be shown in the UI. They will be rendered after the entire HTML view rendering process, and can be accessed and utilized in the ngAfterViewInit method to display the template columns in the child grid.
During the load event of the child grid, it is necessary to set the registeredTemplate to empty. This action will remove any previously existing templates. By doing so, templates can be dynamically applied to the grid’s cells based on different conditions or requirements.
In the example below, a custom image is rendered in the “Employee Image” column of the child grid using the template property.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GridModule } from '@syncfusion/ej2-angular-grids'
import { PageService, SortService, FilterService, GroupService,DetailRowService } from '@syncfusion/ej2-angular-grids'
import { Component, OnInit, ViewChild, ViewContainerRef, Inject, AfterViewInit } from '@angular/core';
import { data, employeeData } from './datasource';
@Component({
imports: [GridModule],
providers: [PageService,
SortService,
FilterService,
GroupService,DetailRowService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid [dataSource]='parentData' height='315px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=100></e-column>
</e-columns>
</ejs-grid>
<ng-template #childtemplate let-data>
<div class="image">
<img src="https://ej2.syncfusion.com/angular/demos/assets/grid/images/{{data.EmployeeID}}.png" alt="{{ data.EmployeeID }}"/>
</div>
</ng-template>`,
})
export class AppComponent implements OnInit, AfterViewInit {
constructor(@Inject(ViewContainerRef) private viewContainerRef?: ViewContainerRef) {
}
public parentData?: object[];
@ViewChild('childtemplate' , { static: true }) public childtemplate?: any;
public childGrid?: any;
ngAfterViewInit() {
this.childtemplate.elementRef.nativeElement._viewContainerRef = this.viewContainerRef;
this.childtemplate.elementRef.nativeElement.propName = 'template';
}
ngOnInit(): void {
this.parentData = employeeData;
this.childGrid = {
dataSource: data,
queryString: 'EmployeeID',
load() {
this.registeredTemplate = {}; // set registerTemplate value as empty in load event
},
columns: [
{ headerText: 'Employee Image', textAlign: 'Center', template: this.childtemplate, width: 120 },
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
};
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Getting parent details in the child grid
Accessing parent row details within a child grid enables contextual data presentation and supports scenarios where parent information is required for calculations or conditional rendering.
This can be accomplished through the grid’s created event, which is triggered when the child grid is initialized. Within this event, parent row data can be obtained using this.parentDetails.parentRowData. The example below demonstrates accessing parent details for use in the child grid.
import { data, employeeData, ParentDetailsDataType } from './datasource';
import { Component, OnInit } from '@angular/core';
import { DetailRowService, FilterService, GridModel, GridModule, PageService, ParentDetails, SortService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [GridModule],
providers: [PageService, SortService, FilterService, DetailRowService],
standalone: true,
selector: 'app-root',
template: `
<div style="margin-left:100px;">
<p style="color:black; font-size: large;" id="message"></p>
</div>
<ejs-grid #grid [dataSource]='parentData' height='315px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=120></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public message?: string;
public parentDetails?: ParentDetails[];
public childGrid: GridModel = {
dataSource: data,
queryString: 'EmployeeID',
created: this.created,
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
};
created() {
var parentRowData = (this.parentDetails as ParentDetails).parentRowData; // 'this' refers to the instance of the child grid.
(document.getElementById('message') as HTMLElement).innerHTML = `EmployeeID: ${(parentRowData as ParentDetailsDataType).EmployeeID}, FirstName: ${(parentRowData as ParentDetailsDataType).FirstName}, Title: ${(parentRowData as ParentDetailsDataType).Title}`;
}
ngOnInit(): void {
this.parentData = employeeData;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Render aggregates in child grid
The Aggregates feature in the Data Grid component allows displaying aggregate values in the footer, group footer, and group caption of the child grid. With this feature, calculations can be easily performed on specific columns and summary information can be shown.
Rendering aggregates in a child grid involves displaying summary data at the footer or group caption of the grid. This can be particularly useful in hierarchical grids where each child grid represents detailed data that needs to be summarized.
The following example demonstrates rendering aggregates in a child grid to display the sum and maximum values of the “Freight” column.
import { data, employeeData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { AggregateService, DetailRowService, FilterService, GridModel, GridModule, GroupService, PageService, SortService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [ GridModule ],
providers: [ PageService, SortService, FilterService, GroupService, DetailRowService, AggregateService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid [dataSource]='parentData' height='315px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=120></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public childGrid: GridModel = {
dataSource: data,
queryString: 'EmployeeID',
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'Freight', headerText: 'Freight', format:'C2', width: 120 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
aggregates: [
{
columns: [
{
type: 'Sum',
field: 'Freight',
format:'C2',
footerTemplate: 'Sum: ${Sum}',
},
],
},
{
columns: [
{
type: 'Max',
field: 'Freight',
format:'C2',
footerTemplate: 'Max: ${Max}',
},
],
},
],
};
ngOnInit(): void {
this.parentData = employeeData;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Expand and collapse all child grids dynamically
The Hierarchy Grid in the Data Grid component supports expanding all child grid rows through an external button, providing a comprehensive view of hierarchical data without requiring manual interaction.
By default, all child grids are rendered in a collapsed state. To programmatically expand or collapse all child rows, use the expandAll and collapseAll methods provided by the DetailRow module. The following example demonstrates triggering these methods via external button clicks.
import { data, employeeData } from './datasource';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { DetailRowService, FilterService, GridComponent, GridModel, GridModule, GroupService, PageService, SortService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [ GridModule, ButtonModule ],
providers: [PageService, SortService, FilterService, GroupService, DetailRowService],
standalone: true,
selector: 'app-root',
template: `<button ejs-button (click)='expand()'>Expand All</button>
<button ejs-button (click)='collapse()'>Collapse All</button>
<ejs-grid #grid [dataSource]='parentData' height='265px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=100></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public childGrid: GridModel = {
dataSource: data,
queryString: 'EmployeeID',
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
};
@ViewChild('grid')
public grid?: GridComponent;
ngOnInit(): void {
this.parentData = employeeData;
}
expand(): void {
(this.grid as GridComponent).detailRowModule.expandAll();
}
collapse(): void {
(this.grid as GridComponent).detailRowModule.collapseAll();
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));The
expandAllandcollapseAllmethods are not recommended for large datasets due to the considerable time required to update the changes in the UI.
Change hierarchy grid icon in Grid
The default expand/collapse icons in the Hierarchy Grid can be customized by applying custom CSS to the expand and collapse icon classes. Add the following styles to the index.css file to replace the default icons with custom ones.
.e-grid .e-icon-grightarrow::before,
.e-grid-menu .e-icon-grightarrow::before {
content: '\e85f';
}
.e-grid .e-icon-gdownarrow::before,
.e-grid-menu .e-icon-gdownarrow::before {
content: '\e83f';
}In the demo below, the expand/collapse icons have been changed to arrow-down and arrow-right icons.
import { data, employeeData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { DetailRowService, GridModel, GridModule, PageService } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [ GridModule],
providers: [PageService, DetailRowService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid [dataSource]='parentData' height='315px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=80></e-column>
<e-column field='FirstName' headerText='FirstName' width=100></e-column>
<e-column field='LastName' headerText='Last Name' width=100></e-column>
<e-column field='City' headerText='City' width=120></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public parentData?: object[];
public childGrid: GridModel = {
dataSource: data,
queryString: 'EmployeeID',
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 90 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 100 },
{ field: 'ShipCity', headerText: 'Ship City', width: 100 },
{ field: 'ShipName', headerText: 'Ship Name', width: 120 }
],
};
ngOnInit(): void {
this.parentData = employeeData;
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Hide the expand/collapse icon in parent row when no record in child grid
The Data Grid supports hiding the expand/collapse icon in parent rows when there are no child records, providing a cleaner and more intuitive interface.
To achieve this, the rowDataBound event can be utilized to hide the icon when there are no records in the child grid.
To hide the expand/collapse icon in parent row when no records in child grid, follow the given steps:
-
Create a CSS class with custom style: Define a CSS class to override the default appearance of the expand/collapse cell. This style is used to adjust the look of the parent row when selected or hovered.
.e-row[aria-selected="true"] .e-customizedexpandcell { background-color: #e0e0e0; } .e-grid.e-gridhover tr[role='row']:hover { background-color: #eee; } -
Implement the
rowDataBoundevent handler: TherowDataBoundevent is triggered for each row as data is bound. In this event, verify whether the parent row has any corresponding child records. If no child records are found, clear the cell containing the expand/collapse icon and apply the custom CSS class.public rowDataBound(args: RowDataBoundEventArgs) { const parentData: number = (args.data as Employee)['EmployeeID']; const childrecord: object[] = new DataManager(childData as JSON[]). executeLocal(new Query().where('EmployeeID', 'equal', parentData, true)); if (childrecord.length === 0) { // Here hide which parent row has no child records const rowElement = args.row as HTMLTableRowElement; const cellElement= rowElement.querySelector('td') as HTMLTableCellElement cellElement.innerHTML = ' '; cellElement.className = 'e-customizedexpandcell'; } }
The following example demonstrates hiding the expand/collapse icon for the row where the “Employee ID” is “1”, since it has no corresponding child records.
import { childData, employeeData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';
import { DetailRowService, GridModel, GridModule, PageService, RowDataBoundEventArgs } from '@syncfusion/ej2-angular-grids';
import { DataManager, Query } from '@syncfusion/ej2-data';
interface Employee {
EmployeeID: number;
FirstName: string;
City: string;
Country: string;
}
@Component({
imports: [ButtonModule,GridModule],
providers: [PageService,DetailRowService],
standalone: true,
selector: 'app-root',
template: `<ejs-grid [dataSource]='data' [childGrid]='childGrid' (rowDataBound)="rowDataBound($event)">
<e-columns>
<e-column field='EmployeeID' headerText='EmployeeID' width='80' ></e-column>
<e-column field='FirstName' headerText='First Name' width='100' ></e-column>
<e-column field='City' headerText='City' width='100'></e-column>
<e-column field='Country' headerText='Country' width='100'></e-column>
</e-columns>
</ejs-grid>`,
})
export class AppComponent implements OnInit {
public data?: object[];
public childGrid?: GridModel;
ngOnInit(): void {
this.data = employeeData;
this.childGrid = {
dataSource: childData,
queryString: 'EmployeeID',
allowPaging: true,
columns: [
{ field: 'Order', headerText: 'Order ID', textAlign: 'Right', width: 120 },
{ field: 'EmployeeID', headerText: 'Employee ID', textAlign: 'Right', width: 120 },
{ field: 'ShipName', headerText: 'Ship Name', width: 150 }
]
};
}
public rowDataBound(args: RowDataBoundEventArgs) {
const parentData: number = (args.data as Employee)['EmployeeID'];
const childrecord: object[] = new DataManager(childData as JSON[]).
executeLocal(new Query().where('EmployeeID', 'equal', parentData, true));
if (childrecord.length === 0) {
// Here hide which parent row has no child records
const rowElement = args.row as HTMLTableRowElement;
const cellElement= rowElement.querySelector('td') as HTMLTableCellElement
cellElement.innerHTML = ' ';
cellElement.className = 'e-customizedexpandcell';
}
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Child Grid expand and collapse events
The Syncfusion® Angular Grid component provides detailExpand and detailCollapse events, triggered before a detail row(parent row) is expanded or collapsed. These events provide control over the expand/collapse behavior by allowing conditional logic through event arguments.
In the example below, expansion is prevented for the “Nancy” row, and collapse is prevented for the “Andrew” row.
import { data, employeeData } from './datasource';
import { Component, OnInit } from '@angular/core';
import { DetailRowService, GridModel, GridModule } from '@syncfusion/ej2-angular-grids';
@Component({
imports: [ GridModule, ],
providers: [DetailRowService],
standalone: true,
selector: 'app-root',
template: `<div>
<ejs-grid #grid [dataSource]="data" height="335" [childGrid]='childGrid' (detailExpand)="detailExpand($event)" (detailCollapse)="detailCollapse($event)">
<e-columns>
<e-column field="EmployeeID" headerText="Employee ID" textAlign="Right" width="120"></e-column>
<e-column field="FirstName" headerText="First Name" width="150"></e-column>
<e-column field="City" headerText="City" width="150"></e-column>
<e-column field="Country" headerText="Country" width="150"></e-column>
</e-columns>
</ejs-grid>
</div>`
})
export class AppComponent implements OnInit {
public data?: object[];
public childGrid?: GridModel;
ngOnInit(): void {
this.data = employeeData;
this.childGrid= {
dataSource: data,
queryString: 'EmployeeID',
columns: [
{ field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 120 },
{ field: 'CustomerID', headerText: 'Customer ID', width: 150 },
{ field: 'ShipCity', headerText: 'Ship City', width: 150 },
{ field: 'ShipName', headerText: 'Ship Name', width: 150 }
],
}
}
// Prevent expanding detail row.
public detailExpand(args: any): void {
if (args.rowData.FirstName === 'Nancy') {
args.cancel = true;
}
}
// Prevent collapsing detail row.
public detailCollapse(args: any): void {
if (args.rowData.FirstName === 'Andrew') {
args.cancel = true;
}
}
}import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import 'zone.js';
bootstrapApplication(AppComponent).catch((err) => console.error(err));Customize the child grid
The child grid’s appearance within the parent grid can be customized using CSS. The .e-detailcell selector targets the child grid container and enables modification of properties such as background color, borders, and font styles.
Header
The appearance of the header elements in the child grid can be customized using CSS. Here are examples for customizing the child grid header, header cell, and header cell div element.
Customizing the child grid header
To modify the header of the child grid, use the following CSS. The .e-detailcell class targets the child grid, and .e-headercontent targets the header container. Updating the border property customizes the line between the header and content.
.e-detailcell .e-grid .e-headercontent{
border: 2px solid green;
}
Customizing the child grid header cell
To style the child grid’s header cells, use the following CSS. The .e-headercell selector targets the header cell, allows to change the text color and background.
.e-detailcell .e-grid .e-headercontent .e-headercell{
color: #ffffff;
background-color: #1ea8bd;
}
Customizing the child grid header cell div element
To style the inner div of the child grid’s header cells, use the following CSS. The .e-headercelldiv selector targets the header cell content and allows customization of font size, weight, and color.
.e-detailcell .e-grid .e-headercelldiv {
font-size: 15px;
font-weight: bold;
color: darkblue;
}
Paging
The paging elements of the child grid can be customized using CSS. The following examples show styling for the pager root, pager container, navigation elements, numeric links, and the current page indicator.
Customizing the child grid pager root element
To style the child grid’s pager root, use the following CSS. The .e-detailcell class targets the child grid, and .e-gridpager selects the pager container. The font-family and background-color properties define the font and background styling.
.e-detailcell .e-grid .e-gridpager {
font-family: cursive;
background-color: #deecf9;
}
Customizing the child grid pager container element
To style the child grid’s pager container, apply the following CSS. The .e-pagercontainer selector modifies the border and font of the pager section.
.e-detailcell .e-grid .e-pagercontainer {
border: 2px solid #00b5ff;
font-family: cursive;
}
Customizing the child grid pager navigation elements
To style the child grid’s pager navigation buttons, apply the following CSS. These selectors target all navigation states, allowing customization of their background-color.
.e-detailcell .e-grid .e-gridpager .e-prevpagedisabled,
.e-detailcell .e-grid .e-gridpager .e-prevpage,
.e-detailcell .e-grid .e-gridpager .e-nextpage,
.e-detailcell .e-grid .e-gridpager .e-nextpagedisabled,
.e-detailcell .e-grid .e-gridpager .e-lastpagedisabled,
.e-detailcell .e-grid .e-gridpager .e-lastpage,
.e-detailcell .e-grid .e-gridpager .e-firstpage,
.e-detailcell .e-grid .e-gridpager .e-firstpagedisabled {
background-color: #deecf9;
}
Customizing the child grid pager page numeric link elements
To style the child grid’s pager numeric link elements, use the following CSS. The .e-numericitem selector targets each page number, allowing customization of background-color, color, and hover effects.
To customize the appearance of the child grid pager page numeric link elements, the following CSS code can be used:
.e-detailcell .e-grid .e-gridpager .e-numericitem {
background-color: #5290cb;
color: #ffffff;
cursor: pointer;
}
.e-detailcell .e-grid .e-gridpager .e-numericitem:hover {
background-color: white;
color: #007bff;
}
Customizing the child grid pager current page numeric element
To style the current page number in the child grid pager, use the following CSS. The .e-currentitem selector targets the active numeric item and allows customization of its background-color and color.
To customize the appearance of the child grid pager current page numeric element, the following CSS code can be used:
.e-detailcell .e-grid .e-gridpager .e-currentitem {
background-color: #0078d7;
color: #fff;
}
Sorting
The appearance of sorting icons in the child grid can be customized using CSS. Syncfusion® provides a set of built-in icons that can be used based on the theme.
Customizing the child grid sorting icon
To change the sorting icons in the child grid header, apply the following CSS. The .e-icon-ascending::before and .e-icon-descending::before selectors target the icons for ascending and descending sort states, allowing custom icon codes.
.e-detailcell .e-grid .e-icon-ascending::before {
content: '\e7a3';
/* Icon code for ascending order */
}
.e-detailcell .e-grid .e-icon-descending::before {
content: '\e7b6';
/* Icon code for descending order */
}![]()
Customizing the child grid multi sorting icon
To style the multi sorting icon in the child grid header, apply the following CSS. The .e-sortnumber selector targets the icon, allowing customization of its background-color and font style.
.e-detailcell .e-grid .e-sortnumber {
background-color: #deecf9;
font-family: cursive;
}![]()
Filtering
The appearance of filtering elements in the child grid can be customized using CSS. The following examples demonstrate styling key filtering components, including filter bar cells, filter inputs, focus states, clear icons, filter icons, filter dialog content and footer, dialog input elements, dialog buttons, and excel filter number options.
Customizing the child grid filter bar cell element
To style the filter bar cell in the child grid header, apply the following CSS. The .e-filterbarcell selector targets the cell, allowing customization of its background-color.
.e-detailcell .e-grid .e-filterbar .e-filterbarcell {
background-color: #045fb4;
}
Customizing the child grid filter bar input element
To style the input field within the child grid’s filter bar, use the following CSS. The .e-input selector targets the input element, allowing customization of its font.
.e-detailcell .e-grid .e-filterbarcell .e-input-group input.e-input{
font-family: cursive;
}
Customizing the child grid filter bar input focus
To style the focus highlight of the filter bar input in the child grid, use the following CSS. The .e-input-focus selector targets the input when it’s active, allowing customization of its background-color.
.e-detailcell .e-grid .e-filterbarcell .e-input-group.e-input-focus{
background-color: #deecf9;
}
Customizing the child grid filter bar input clear icon
To modify the clear icon in the child grid’s filter bar input, apply the following CSS. The .e-clear-icon::before selector allows changing the icon using a custom Unicode value.
.e-detailcell .e-grid .e-filterbarcell .e-input-group .e-clear-icon::before {
content: '\e72c';
}![]()
Customizing the child grid filtering icon
To style the filtering icon in the child grid header, use the .e-icon-filter::before selector. The content property defines the icon and can be updated to display a different symbol.
.e-detailcell .e-grid .e-icon-filter::before{
content: '\e81e';
}![]()
Customizing the child grid filter dialog content
To style the content area of the child grid’s filter dialog, use the .e-filter-popup .e-dlg-content selector with the background-color property.
.e-detailcell .e-grid .e-filter-popup .e-dlg-content {
background-color: #deecf9;
}
Customizing the child grid filter dialog footer
To customize the footer of the child grid’s filter dialog, apply the background-color property to the .e-filter-popup .e-footer-content element using the following CSS.
.e-detailcell .e-grid .e-filter-popup .e-footer-content {
background-color: #deecf9;
}
Customizing the child grid filter dialog input element
To customize the input elements in the child grid’s filter dialog, apply the font-family property to the .e-filter-popup .e-input selector using the following CSS.
.e-detailcell .e-grid .e-filter-popup .e-input-group input.e-input{
font-family: cursive;
}
Customizing the child grid filter dialog button element
The filter dialog’s buttons can be styled by targeting .e-filter-popup and .e-btn, applying a font-family to change their font.
.e-detailcell .e-grid .e-filter-popup .e-btn{
font-family: cursive;
}
Customizing the child grid excel filter dialog number filters element
The number filter options in the child grid’s excel filter dialog can be styled by applying a background-color to the .e-filter-popup .e-contextmenu-wrapper ul element as below:
.e-detailcell .e-grid .e-filter-popup .e-contextmenu-wrapper ul{
background-color: #deecf9;
}
Grouping
The grouping-related elements in the child grid can be styled through CSS. This includes the group header, expand/collapse icons, group caption row, and indent cells.
Customizing the child grid group header
To customize the appearance of the child grid’s group header, target the .e-groupdroparea element and apply the desired background-color:
.e-detailcell .e-grid .e-groupdroparea {
background-color: #132f49;
}
Customizing the child grid group expand or collapse icons
To change the expand and collapse icons in the child grid’s group header, use the content property on .e-icon-gdownarrow::before and .e-icon-grightarrow::before:
.e-detailcell .e-grid .e-icon-gdownarrow::before{
content:'\e7c9'
}
.e-detailcell .e-grid .e-icon-grightarrow::before{
content:'\e80f'
}![]()
Customizing the child grid group caption row
To style the child grid’s group caption row and its expand/collapse icons, use the background-color property on the following elements:
.e-detailcell .e-grid .e-groupcaption {
background-color: #deecf9;
}
.e-detailcell .e-grid .e-recordplusexpand,
.e-detailcell .e-grid .e-recordpluscollapse {
background-color: #deecf9;
}
Customizing the child grid grouping indent cell
To style the child grid’s grouping indent cell, target the .e-indentcell element and apply the desired background-color:
.e-detailcell .e-grid .e-indentcell {
background-color: #deecf9;
}
Toolbar
The toolbar in the child grid can be styled through CSS. The following examples demonstrate customization of the toolbar’s root element and its button elements.
Customizing the child grid toolbar root element
The toolbar’s root element in the child grid can be styled by applying a background-color to the .e-toolbar-items selector.
.e-detailcell .e-grid .e-toolbar-items {
background-color: #deecf9;
}
Customizing the child grid toolbar button element
The toolbar buttons in the child grid can be styled by applying a background-color to the .e-toolbar .e-btn selector.
.e-detailcell .e-grid .e-toolbar .e-btn {
background-color: #deecf9;
}
Editing
The appearance of editing-related elements in the child grid can be customized using CSS. This includes input fields, dialog components, and action buttons.
Customizing the child grid edited and added row element
The edited and added rows in the child grid can be styled by applying a background-color to the .e-editedrow table and .e-addedrow table selectors.
.e-detailcell .e-grid .e-editedrow table,
.e-detailcell .e-grid .e-addedrow table {
background-color: #62b2eb;
}

Customizing the child grid edited row input element
To style input fields within edited rows in the child grid, target the .e-input elements inside .e-editedrow and apply properties like font-family and color:
.e-detailcell .e-grid .e-editedrow .e-input-group input.e-input{
font-family: cursive;
color:rgb(214, 33, 123)
}
Customizing the child grid edit dialog header element
The edit dialog’s header in the child grid can be styled by applying a background-color to the .e-edit-dialog .e-dlg-header-content selector.
.e-edit-dialog .e-dlg-header-content {
background-color: #deecf9;
}
Customizing the child grid edited row input element in dialog edit mode
The input fields in dialog edit mode can be styled by applying a font-family to the .e-gridform .e-float-input .e-field selector.
.e-grid .e-gridform .e-rowcell .e-float-input .e-field {
font-family: cursive;
}
Customizing the child grid command column buttons
To style the command column buttons (edit, delete, update, and cancel) in the child grid, target their respective classes and apply the desired color:
.e-detailcell .e-grid .e-delete::before ,.e-grid .e-cancel-icon::before{
color: #f51717;
}
.e-detailcell .e-grid .e-edit::before, .e-grid .e-update::before {
color: #077005;
}

Aggregate
Aggregate elements in the child grid can be styled through CSS. The following examples demonstrate customization options for both the aggregate root element and individual aggregate cells.
Customizing the child grid aggregate root element
Styling the child grid’s aggregate root element can be achieved by applying CSS properties such as font-family to the .e-gridfooter selector.
.e-detailcell .e-grid .e-gridfooter {
font-family: cursive;
}
Customizing the child grid aggregate cell elements
The .e-summarycell class within the .e-summaryrow of the child grid can be styled using properties like background-color to modify the appearance of individual aggregate cells:
.e-detailcell .e-grid .e-summaryrow .e-summarycell {
background-color: #deecf9;
}
Selection
The appearance of selection within the child grid can be customized by applying CSS properties to modify the background of selected rows, cells, or columns.
Customizing the child grid row selection background
The .e-selectionbackground class within the child grid can be styled using background-color property to modify the appearance of selected rows:
.e-detailcell .e-grid td.e-selectionbackground {
background-color: #00b7ea;
}
Customizing the child grid cell selection background
The cell selection background in the child grid can be customized by applying the background-color property to the td.e-cellselectionbackground selector.
.e-detailcell .e-grid td.e-cellselectionbackground {
background-color: #00b7ea;
}
Customizing the child grid column selection background
The column selection background in the child grid can be customized by applying the background-color property to the .e-columnselection selector.
.e-detailcell .e-grid .e-columnselection {
background-color: #aec2ec;
}
See Also
Multiple queryString in hierarchy child grid in Angular Grid