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';
}
}