DevExtreme v26.1 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Angular Data Grid - Master-Detail View

The Master-Detail View allows users to expand a row ('master') and display the related content ('details') in the expanded section. To configure this feature, set the the masterDetail object's enabled property to true and use a template to specify detail section content. In this demo, the detail section displays another DataGrid.

Backend API
<dx-data-grid id="gridContainer" [dataSource]="employees" keyExpr="ID" [showBorders]="true" > <dxi-data-grid-column dataField="Prefix" [width]="70" caption="Title" ></dxi-data-grid-column> <dxi-data-grid-column dataField="FirstName"></dxi-data-grid-column> <dxi-data-grid-column dataField="LastName"></dxi-data-grid-column> <dxi-data-grid-column dataField="Position" [width]="170" ></dxi-data-grid-column> <dxi-data-grid-column dataField="State" [width]="125"></dxi-data-grid-column> <dxi-data-grid-column dataField="BirthDate" dataType="date" ></dxi-data-grid-column> <dxo-data-grid-master-detail [enabled]="true" template="detail" ></dxo-data-grid-master-detail> <div *dxTemplate="let employee of 'detail'"> <div class="master-detail-caption">{{ employee.data.FirstName + " " + employee.data.LastName + "'s Tasks:" }}</div> <detail-grid [key]="employee.key"></detail-grid> </div> </dx-data-grid>
import { bootstrapApplication } from '@angular/platform-browser'; import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/core'; import { DxDataGridModule } from 'devextreme-angular'; import { Employee, Service } from './app.service'; import { DetailGridComponent } from './detail-grid/detail-grid.component'; if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [Service], imports: [ DxDataGridModule, DetailGridComponent, ], }) export class AppComponent { employees: Employee[]; constructor(private service: Service) { this.employees = service.getEmployees(); } } bootstrapApplication(AppComponent, { providers: [ provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }), ], });
::ng-deep #gridContainer { height: 440px; } ::ng-deep .master-detail-caption { padding: 0 0 5px 10px; font-size: 14px; font-weight: bold; }
import { Injectable } from '@angular/core'; export class Task { ID: number; Subject: string; StartDate: string; DueDate: string; Status: string; Priority: string; Completion: number; EmployeeID: number; } export class Employee { ID: number; FirstName: string; LastName: string; Prefix: string; Position: string; BirthDate: string; State: string; } const employees: Employee[] = [{ ID: 1, Prefix: 'Mr.', FirstName: 'John', LastName: 'Heart', Position: 'CEO', State: 'California', BirthDate: '1964/03/16', }, { ID: 2, Prefix: 'Mrs.', FirstName: 'Olivia', LastName: 'Peyton', Position: 'Sales Assistant', State: 'California', BirthDate: '1981/06/03', }, { ID: 3, Prefix: 'Mr.', FirstName: 'Robert', LastName: 'Reagan', Position: 'CMO', State: 'Arkansas', BirthDate: '1974/09/07', }, { ID: 4, Prefix: 'Ms.', FirstName: 'Greta', LastName: 'Sims', Position: 'HR Manager', State: 'Georgia', BirthDate: '1977/11/22', }, { ID: 5, Prefix: 'Mr.', FirstName: 'Brett', LastName: 'Wade', Position: 'IT Manager', State: 'Idaho', BirthDate: '1968/12/01', }, { ID: 6, Prefix: 'Mrs.', FirstName: 'Sandra', LastName: 'Johnson', Position: 'Controller', State: 'Utah', BirthDate: '1974/11/15', }, { ID: 7, Prefix: 'Mr.', FirstName: 'Kevin', LastName: 'Carter', Position: 'Shipping Manager', State: 'California', BirthDate: '1978/01/09', }, { ID: 8, Prefix: 'Ms.', FirstName: 'Cynthia', LastName: 'Stanwick', Position: 'HR Assistant', State: 'Arkansas', BirthDate: '1985/06/05', }, { ID: 9, Prefix: 'Dr.', FirstName: 'Kent', LastName: 'Samuelson', Position: 'Ombudsman', State: 'Missouri', BirthDate: '1972/09/11', }]; const tasks: Task[] = [{ ID: 1, Subject: 'Prepare 2023 Financial', StartDate: '2023/01/15', DueDate: '2023/01/31', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 8, }, { ID: 2, Subject: 'Prepare 2023 Marketing Plan', StartDate: '2023/01/01', DueDate: '2023/01/31', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 5, }, { ID: 3, Subject: 'Update Personnel Files', StartDate: '2023/02/03', DueDate: '2023/02/28', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 2, }, { ID: 4, Subject: 'Review Health Insurance Options Under the Affordable Care Act', StartDate: '2023/02/12', DueDate: '2023/04/25', Status: 'In Progress', Priority: 'High', Completion: 50, EmployeeID: 2, }, { ID: 5, Subject: 'Choose between PPO and HMO Health Plan', StartDate: '2023/02/15', DueDate: '2023/04/15', Status: 'In Progress', Priority: 'High', Completion: 75, EmployeeID: 1, }, { ID: 6, Subject: 'Google AdWords Strategy', StartDate: '2023/02/16', DueDate: '2023/02/28', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 1, }, { ID: 7, Subject: 'New Brochures', StartDate: '2023/02/17', DueDate: '2023/02/24', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 1, }, { ID: 11, Subject: 'Rollout of New Website and Marketing Brochures', StartDate: '2023/02/20', DueDate: '2023/02/28', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 5, }, { ID: 12, Subject: 'Update Sales Strategy Documents', StartDate: '2023/02/20', DueDate: '2023/02/22', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 9, }, { ID: 15, Subject: 'Review 2022 Sales Report and Approve 2023 Plans', StartDate: '2023/02/23', DueDate: '2023/02/28', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 5, }, { ID: 16, Subject: 'Deliver R&D Plans for 2023', StartDate: '2023/03/01', DueDate: '2023/03/10', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 3, }, { ID: 20, Subject: 'Approve Hiring of John Jeffers', StartDate: '2023/03/02', DueDate: '2023/03/12', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 4, }, { ID: 20, Subject: 'Approve Hiring of John Jeffers', StartDate: '2023/03/02', DueDate: '2023/03/12', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 6, }, { ID: 21, Subject: 'Non-Compete Agreements', StartDate: '2023/03/12', DueDate: '2023/03/14', Status: 'Completed', Priority: 'Low', Completion: 100, EmployeeID: 2, }, { ID: 22, Subject: 'Update NDA Agreement', StartDate: '2023/03/14', DueDate: '2023/03/16', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 1, }, { ID: 23, Subject: 'Update Employee Files with New NDA', StartDate: '2023/03/16', DueDate: '2023/03/26', Status: 'Need Assistance', Priority: 'Normal', Completion: 90, EmployeeID: 4, }, { ID: 24, Subject: 'Update Employee Files with New NDA', StartDate: '2023/03/16', DueDate: '2023/03/26', Status: 'Need Assistance', Priority: 'Normal', Completion: 90, EmployeeID: 6, }, { ID: 25, Subject: 'Sign Updated NDA', StartDate: '2023/03/20', DueDate: '2023/03/25', Status: 'Completed', Priority: 'Urgent', Completion: 100, EmployeeID: 7, }, { ID: 26, Subject: 'Sign Updated NDA', StartDate: '2023/03/20', DueDate: '2023/03/25', Status: 'Completed', Priority: 'Urgent', Completion: 100, EmployeeID: 8, }, { ID: 27, Subject: 'Sign Updated NDA', StartDate: '2023/03/20', DueDate: '2023/03/25', Status: 'Need Assistance', Priority: 'Urgent', Completion: 25, EmployeeID: 9, }, { ID: 35, Subject: 'Update Revenue Projections', StartDate: '2023/03/24', DueDate: '2023/04/07', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 8, }, { ID: 36, Subject: 'Review Revenue Projections', StartDate: '2023/03/25', DueDate: '2023/04/06', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 9, }, { ID: 40, Subject: 'Provide New Health Insurance Docs', StartDate: '2023/03/28', DueDate: '2023/04/07', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 4, }, { ID: 41, Subject: 'Provide New Health Insurance Docs', StartDate: '2023/03/28', DueDate: '2023/04/07', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 6, }, { ID: 50, Subject: 'Give Final Approval for Refunds', StartDate: '2023/05/05', DueDate: '2023/05/15', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 2, }, { ID: 52, Subject: 'Review Product Recall Report by Engineering Team', StartDate: '2023/05/17', DueDate: '2023/05/20', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 1, }, { ID: 55, Subject: 'Review Overtime Report', StartDate: '2023/06/10', DueDate: '2023/06/14', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 7, }, { ID: 60, Subject: 'Refund Request Template', StartDate: '2023/06/17', DueDate: '2024/04/01', Status: 'Deferred', Priority: 'Normal', Completion: 0, EmployeeID: 9, }, { ID: 71, Subject: 'Upgrade Server Hardware', StartDate: '2023/07/22', DueDate: '2023/07/31', Status: 'Completed', Priority: 'Urgent', Completion: 100, EmployeeID: 7, }, { ID: 72, Subject: 'Upgrade Personal Computers', StartDate: '2023/07/24', DueDate: '2024/04/30', Status: 'In Progress', Priority: 'Normal', Completion: 85, EmployeeID: 7, }, { ID: 74, Subject: 'Decide on Mobile Devices to Use in the Field', StartDate: '2023/07/30', DueDate: '2023/08/02', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 3, }, { ID: 78, Subject: 'Try New Touch-Enabled WinForms Apps', StartDate: '2023/08/11', DueDate: '2023/08/15', Status: 'Completed', Priority: 'Normal', Completion: 100, EmployeeID: 3, }, { ID: 81, Subject: 'Review Site Up-Time Report', StartDate: '2023/08/24', DueDate: '2023/08/30', Status: 'Completed', Priority: 'Urgent', Completion: 100, EmployeeID: 5, }, { ID: 99, Subject: 'Submit D&B Number to ISP for Credit Approval', StartDate: '2023/11/04', DueDate: '2023/11/07', Status: 'Completed', Priority: 'High', Completion: 100, EmployeeID: 8, }, { ID: 117, Subject: 'Approval on Converting to New HDMI Specification', StartDate: '2024/01/11', DueDate: '2024/01/31', Status: 'Deferred', Priority: 'Normal', Completion: 75, EmployeeID: 3, }, { ID: 138, Subject: 'Review HR Budget Company Wide', StartDate: '2024/03/20', DueDate: '2024/03/25', Status: 'In Progress', Priority: 'Normal', Completion: 40, EmployeeID: 6, }, { ID: 145, Subject: 'Final Budget Review', StartDate: '2024/03/26', DueDate: '2024/03/27', Status: 'In Progress', Priority: 'High', Completion: 25, EmployeeID: 6, }]; @Injectable() export class Service { getEmployees() { return employees; } getTasks() { return tasks; } }
<!DOCTYPE html> <html lang="en"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/26.1.5/css/dx.light.css" /> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> <script src="https://cdn.jsdelivr.net/npm/zone.js@0.16.2/bundles/zone.umd.js"></script> <script src="./bundle.js" type="module"></script> </body> </html>
<dx-data-grid [dataSource]="tasksDataSource" [showBorders]="true" [columnAutoWidth]="true" > <dxi-data-grid-column dataField="Subject" dataType="string" ></dxi-data-grid-column> <dxi-data-grid-column dataField="StartDate" dataType="date" ></dxi-data-grid-column> <dxi-data-grid-column dataField="DueDate" dataType="date" ></dxi-data-grid-column> <dxi-data-grid-column dataField="Priority" dataType="string" ></dxi-data-grid-column> <dxi-data-grid-column caption="Completed" dataType="boolean" [calculateCellValue]="completedValue" ></dxi-data-grid-column> </dx-data-grid>
import { Component, Input, AfterViewInit, ChangeDetectorRef } from '@angular/core'; import { DxDataGridModule } from 'devextreme-angular'; import { DataSource, ArrayStore } from 'devextreme-angular/common/data'; import { Service, Task } from '../app.service'; @Component({ selector: 'detail-grid', templateUrl: './detail-grid.component.html', imports: [DxDataGridModule], providers: [Service], }) export class DetailGridComponent implements AfterViewInit { @Input() key: number; tasksDataSource: DataSource; tasks: Task[]; constructor(private service: Service, private changeDetectorRef: ChangeDetectorRef) { this.tasks = service.getTasks(); } ngAfterViewInit() { this.tasksDataSource = new DataSource({ store: new ArrayStore({ data: this.tasks, key: 'ID', }), filter: ['EmployeeID', '=', this.key], }); this.changeDetectorRef.detectChanges(); } completedValue(rowData) { return rowData.Status === 'Completed'; } }