The Grid allows display of table data in a hierarchical structure to visualize relations between parent and child records.
This feature is enabled by defining the childGrid
and
childGrid.queryString
.
The childGrid
describes the options of grid and the
childGrid.queryString
describes the relation between parent and child grids.
To use hierarchical binding, inject the DetailRowService in the provider section of AppModule.
import { Component, OnInit } from '@angular/core';
import { data, employeeData } from './datasource';
import { DetailRowService, GridModel } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='pData' height='315px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=120></e-column>
<e-column field='FirstName' headerText='FirstName' width=150></e-column>
<e-column field='LastName' headerText='Last Name' width=150></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>
`,
providers: [DetailRowService]
})
export class AppComponent implements OnInit {
public pData: object[];
public childGrid: GridModel = {
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 }
],
};
ngOnInit(): void {
this.pData = employeeData;
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';
/**
* Module
*/
@NgModule({
imports: [
BrowserModule,
GridModule
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [PageService,
SortService,
FilterService,
GroupService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
- Grid supports n level of child grids.
- Hierarchical binding is not supported when
DetailTemplate
is enabled.
By default, grid renders in collapsed state.
You can expand all child grid rows by invoking the expandAll
method,
and collapse all grid rows by invoking the collapseAll
through an external button.
import { Component, OnInit, ViewChild } from '@angular/core';
import { data, employeeData } from './datasource';
import { DetailRowService, GridModel, GridComponent } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-root',
template: `<button ej-button (click)='expand()'>Expand All</button>
<button ej-button (click)='collapse()'>Collapse All</button>
<ejs-grid #grid [dataSource]='pData' height='265px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=120></e-column>
<e-column field='FirstName' headerText='FirstName' width=150></e-column>
<e-column field='LastName' headerText='Last Name' width=150></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>
`,
providers: [DetailRowService]
})
export class AppComponent implements OnInit {
public pData: object[];
public childGrid: GridModel = {
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 }
],
};
@ViewChild('grid')
public grid: GridComponent;
ngOnInit(): void {
this.pData = employeeData;
}
expand(): void {
this.grid.detailRowModule.expandAll();
}
collapse(): void {
this.grid.detailRowModule.collapseAll();
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';
/**
* Module
*/
@NgModule({
imports: [
BrowserModule,
GridModule
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [PageService,
SortService,
FilterService,
GroupService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
You can expand a particular child grid at initial rendering by invoking the
expand
method in the dataBound
event.
import { Component, OnInit, ViewChild } from '@angular/core';
import { data, employeeData } from './datasource';
import { DetailRowService, GridModel, GridComponent } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='pData' height='265px' [childGrid]='childGrid' (dataBound)='onDataBound()'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=120></e-column>
<e-column field='FirstName' headerText='FirstName' width=150></e-column>
<e-column field='LastName' headerText='Last Name' width=150></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>
`,
providers: [DetailRowService]
})
export class AppComponent implements OnInit {
public pData: object[];
public childGrid: GridModel = {
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 }
],
};
@ViewChild('grid')
public grid: GridComponent;
ngOnInit(): void {
this.pData = employeeData;
}
onDataBound(): void {
this.grid.detailRowModule.expand(2); // initial expand 2 chid Grid.
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';
/**
* Module
*/
@NgModule({
imports: [
BrowserModule,
GridModule
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [PageService,
SortService,
FilterService,
GroupService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
You can dynamically load child grid dataSource by using the
load
event.
import { Component, OnInit, ViewChild } from '@angular/core';
import { data, employeeData } from './datasource';
import { DetailRowService, GridModel, GridComponent } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='pData' height='265px' [childGrid]='childGrid' (load)='onLoad($event)'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=120></e-column>
<e-column field='FirstName' headerText='FirstName' width=150></e-column>
<e-column field='LastName' headerText='Last Name' width=150></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>
`,
providers: [DetailRowService]
})
export class AppComponent implements OnInit {
public pData: object[];
public childGrid: GridModel = {
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 }
],
};
@ViewChild('grid') public grid: GridComponent;
ngOnInit(): void {
this.pData = employeeData;
}
onLoad(): void {
this.grid.childGrid.dataSource = data; // assign data source for child grid.
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';
/**
* Module
*/
@NgModule({
imports: [
BrowserModule,
GridModule
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [PageService,
SortService,
FilterService,
GroupService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
By default, Parent and child grid relation will be maintained by queryString
property. We should use the same field name to map both parent and child grid. To achieve parent and child relation with different fields, we need to change the mapping value in the child grid load
event.
In the below sample, we have bound the child and parent grid with different fields. Parent grid field name as EmployeeID and the child grid field name as ID. We need to define the mapping value of parentKeyFieldValue from the parent row data in the child grid load
event.
import { Component, OnInit, ViewChild } from '@angular/core';
import { data, employeeData } from './datasource';
import { DetailRowService, GridModel, GridComponent} from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='pData' height='265px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=120></e-column>
<e-column field='FirstName' headerText='FirstName' width=150></e-column>
<e-column field='LastName' headerText='Last Name' width=150></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>
`,
providers: [DetailRowService]
})
export class AppComponent implements OnInit {
@ViewChild('grid') public grid: GridComponent;
public pData: object[];
public childGrid: GridModel | GridComponent = {
queryString: 'ID',
dataSource: data,
columns: [
{ field: 'ID', 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 }
],
load() {
const EmployeeID = 'EmployeeID';
(this as GridComponent).parentDetails.parentKeyFieldValue = (<{ EmployeeID?: string}>(this as GridComponent).parentDetails.parentRowData)[EmployeeID];
}
};
ngOnInit(): void {
this.pData = employeeData;
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';
/**
* Module
*/
@NgModule({
imports: [
BrowserModule,
GridModule
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [PageService,
SortService,
FilterService,
GroupService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
Parent and child grid are related by queryString
field value.
To maintain this relation in newly added record, You need to set value for queryString
field in the added data
by the actionBegin
event.
In the below demo, EmployeeID field relates the parent and child grids. To add a new record in child grid, We have to set the EmployeeID field
with parent record’s queryString
field value in the actionBegin
event.
import { Component, OnInit, ViewChild } from '@angular/core';
import { data, employeeData } from './datasource';
import { DetailRowService, EditService, ToolbarService, AddEventArgs, GridModel, GridComponent } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='pData' height='265px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=120></e-column>
<e-column field='FirstName' headerText='FirstName' width=150></e-column>
<e-column field='LastName' headerText='Last Name' width=150></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>
`,
providers: [DetailRowService, EditService, ToolbarService]
})
export class AppComponent implements OnInit {
public pData: 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: 120 },
{ field: 'EmployeeID', headerText: 'Employee ID', textAlign: 'Right', allowEditing: false, width: 120 },
{ field: 'ShipCity', headerText: 'Ship City', width: 150 },
{ field: 'ShipName', headerText: 'Ship Name', width: 150 }
],
actionBegin(args: AddEventArgs) {
if (args.requestType === 'add') {
// `parentKeyFieldValue` refers to the queryString field value of the parent record.
const EmployeeID = 'EmployeeID';
(args.data as object)[EmployeeID] = this.parentDetails.parentKeyFieldValue;
}
}
};
@ViewChild('grid') public grid: GridComponent;
ngOnInit(): void {
this.pData = employeeData;
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';
/**
* Module
*/
@NgModule({
imports: [
BrowserModule,
GridModule
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [PageService,
SortService,
FilterService,
GroupService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
The column template
has options to display custom element instead of a field value in the column.
In the below sample, we have shown custom image in Employee Image column of child grid using column template
property.
import { Component, OnInit, ViewChild, ViewContainerRef, Inject, AfterViewInit } from '@angular/core';
import { data, employeeData } from './datasource';
import { DetailRowService, GridComponent } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='pData' height='315px' [childGrid]='childGrid'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=120></e-column>
<e-column field='FirstName' headerText='FirstName' width=150></e-column>
<e-column field='LastName' headerText='Last Name' width=150></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>
<ng-template #childtemplate let-data>
<div class="image">
<img src="{{data.EmployeeID}}.png" alt="{{data.EmployeeID}}"/>
</div>
</ng-template>
`,
providers: [DetailRowService]
})
export class AppComponent implements OnInit, AfterViewInit {
constructor(@Inject(ViewContainerRef) private viewContainerRef?: ViewContainerRef) {
}
public pData: object[];
@ViewChild('childtemplate') public childtemplate: any;
@ViewChild('grid') public grid: GridComponent;
public childGrid: any;
ngAfterViewInit() {
this.childtemplate.elementRef.nativeElement._viewContainerRef = this.viewContainerRef;
this.childtemplate.elementRef.nativeElement.propName = 'template';
}
ngOnInit(): void {
this.pData = 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: 150 },
{ 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 }
],
};
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';
/**
* Module
*/
@NgModule({
imports: [
BrowserModule,
GridModule
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [PageService,
SortService,
FilterService,
GroupService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);
By default, the childGrid.queryString describes the relationship between parent and child grids and visualizes the data in a hierarchical structure. Instead of the queryString
property, we can dynamically bind the datasource to the childGrid
based on the parent row data using the detailDataBound event of the grid.
While expanding the child Grid, the detailDataBound
event will be triggered. In this event, based on the EmployeeID column value of parent row data, filter the equally matched data from the childGrid
datasource using the DataManager
plugin and bind the filtered data as a datasource to the childGrid
. This can be demonstrated by the following sample.
import { Component, OnInit, ViewChild } from '@angular/core';
import { data, employeeData } from './datasource';
import { DataManager, Query} from '@syncfusion/ej2-data';
import { DetailRowService, GridModel, GridComponent, DetailDataBoundEventArgs } from '@syncfusion/ej2-angular-grids';
@Component({
selector: 'app-root',
template: `<ejs-grid #grid [dataSource]='pData' height='265px' [childGrid]='childGrid' (detailDataBound)='detailDataBound($event)'>
<e-columns>
<e-column field='EmployeeID' headerText='Employee ID' textAlign='Right' width=120></e-column>
<e-column field='FirstName' headerText='FirstName' width=150></e-column>
<e-column field='LastName' headerText='Last Name' width=150></e-column>
<e-column field='City' headerText='City' width=150></e-column>
</e-columns>
</ejs-grid>
`,
providers: [DetailRowService]
})
export class AppComponent implements OnInit {
public pData: object[];
public childGrid: GridModel = {
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 }
],
};
@ViewChild('grid') public grid: GridComponent;
ngOnInit(): void {
this.pData = employeeData;
}
detailDataBound(args:DetailDataBoundEventArgs) {
var orderData = data;
var empIdValue = args.childGrid.parentDetails.parentRowData.EmployeeID;
var matchedData = new DataManager(orderData).executeLocal(
new Query().where('EmployeeID', 'equal', empIdValue, true)
);
args.childGrid.query = new Query();
args.childGrid.dataSource = matchedData;
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GridModule } from '@syncfusion/ej2-angular-grids';
import { PageService, SortService, FilterService, GroupService } from '@syncfusion/ej2-angular-grids';
import { AppComponent } from './app.component';
/**
* Module
*/
@NgModule({
imports: [
BrowserModule,
GridModule
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [PageService,
SortService,
FilterService,
GroupService]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule);