New to Angular and hopefully someone can help me here. I need to display mat-table where there could be multiple rows of data can be expanded/collapsed. Here is the structure I would like display:
export interface Entity {
name: string;
age: number;
children?: Entity[];
}
const Entities: Entity[] = [
{
name: 'P1',
age: 40,
children: [
{
name: 'C1a',
age: 18
},
{
name: 'C1b',
protocol: 15 }
]
},
{
name: 'P2',
age: 43,
children: [
{
name: 'C2a',
age: 21,
},
{
name: 'C2b',
age: 22
}
]
}
]
In the above, I want to display rows of like this:
name age P1 40 C1a 18 C1b 15 P2 43 C2a 21 C2b 22
When clicking on P1/P2, rows should expand/collapse. Also, all columns should be aligned. Almost all examples I found use outer table and inner table, but that means columns don't get aligned. Is there a way to use single table for everything or align data in the columns somehow?
We can use MatTreeFlatDataSource
for this exact purpose, just add a nested structure as the data source and it will work!
import { FlatTreeControl } from '@angular/cdk/tree';
import { Component } from '@angular/core';
import {
MatTreeFlatDataSource,
MatTreeFlattener,
} from '@angular/material/tree';
export interface Entity {
name: string;
age: number;
children?: Entity[];
}
const TREE_DATA: Entity[] = [
{
name: 'P1',
age: 40,
children: [
{
name: 'C1a',
age: 18,
},
{
name: 'C1b',
age: 15,
},
],
},
{
name: 'P2',
age: 43,
children: [
{
name: 'C2a',
age: 21,
},
{
name: 'C2b',
age: 22,
},
],
},
];
interface ExampleFlatNode {
expandable: boolean;
name: string;
age: number;
level: number;
}
/**
* @title Basic use of `<table mat-table>`
*/
@Component({
selector: 'table-basic-example',
styleUrls: ['table-basic-example.css'],
templateUrl: 'table-basic-example.html',
})
export class TableBasicExample {
displayedColumns: string[] = ['name', 'age'];
private transformer = (node: Entity, level: number) => {
return {
expandable: !!node.children && node.children.length > 0,
name: node.name,
age: node.age,
level: level,
};
};
treeControl = new FlatTreeControl<ExampleFlatNode>(
(node) => node.level,
(node) => node.expandable
);
treeFlattener = new MatTreeFlattener(
this.transformer,
(node) => node.level,
(node) => node.expandable,
(node) => node.children
);
dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
constructor() {
this.dataSource.data = TREE_DATA;
}
hasChild = (_: number, node: ExampleFlatNode) => node.expandable;
}
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>
<span [style.paddingLeft.px]="40"> Name </span>
</th>
<td mat-cell *matCellDef="let data">
<button
mat-icon-button
[style.visibility]="!data.expandable ? 'hidden' : ''"
[style.marginLeft.px]="data.level * 32"
(click)="treeControl.toggle(data)"
>
<mat-icon class="mat-icon-rtl-mirror">
{{treeControl.isExpanded(data) ? 'expand_more' : 'chevron_right'}}
</mat-icon>
</button>
{{data.name}}
</td>
</ng-container>
<ng-container matColumnDef="age">
<th mat-header-cell *matHeaderCellDef>Age</th>
<td mat-cell *matCellDef="let data">{{data.age}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>