I am looking at this Stackblitz template https://stackblitz.com/edit/angular-nested-mat-table?file=app%2Ftable-expandable-rows-example.ts
I am trying to create the same table, however, my data has three layers (i.e. User >> Address >> Block)
Does anyone know how/where I should insert the code to enable the Block expandable layer? (see the code I have tried below, it is still not working though)
Thank you
My current code You may copy and paste into the Stackblitz to try
All the code with the word "Sub" or "Block" is written by me
However the "block" layer is still not showing on the table
Does anyone know why?
table-expandable-rows-example.ts
import { Component, ViewChild, ViewChildren, QueryList, ChangeDetectorRef } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource, MatTable } from '@angular/material/table';
/**
* @title Table with expandable rows
*/
@Component({
selector: 'table-expandable-rows-example',
styleUrls: ['table-expandable-rows-example.css'],
templateUrl: 'table-expandable-rows-example.html',
animations: [
trigger('detailExpand', [
state('collapsed', style({ height: '0px', minHeight: '0' })),
state('expanded', style({ height: '*' })),
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
]),
],
})
export class TableExpandableRowsExample {
@ViewChild('outerSort', { static: true }) sort: MatSort;
@ViewChildren('innerSort') innerSort: QueryList<MatSort>;
@ViewChildren('innerTables') innerTables: QueryList<MatTable<Address>>;
dataSource: MatTableDataSource<User>;
usersData: User[] = [];
columnsToDisplay = ['name', 'email', 'phone'];
innerDisplayedColumns = ['street', 'zipCode', 'city'];
subBlockDisplayedColumns = ['name', 'level', 'unitnumber'];
expandedElement: User | null;
expandedSubElement: Address | null;
constructor(
private cd: ChangeDetectorRef
) { }
ngOnInit() {
USERS.forEach(user => {
if (user.addresses && Array.isArray(user.addresses) && user.addresses.length) {
this.usersData = [...this.usersData, {...user, addresses: new MatTableDataSource(user.addresses)}];
} else {
this.usersData = [...this.usersData, user];
}
});
this.dataSource = new MatTableDataSource(this.usersData);
this.dataSource.sort = this.sort;
}
toggleRow(element: User) {
element.addresses && (element.addresses as MatTableDataSource<Address>).data.length ? (this.expandedElement = this.expandedElement === element ? null : element) : null;
this.cd.detectChanges();
this.innerTables.forEach((table, index) => (table.dataSource as MatTableDataSource<Address>).sort = this.innerSort.toArray()[index]);
}
toggleSubRow(element: Address) {
element.blocks && (element.blocks as MatTableDataSource<Block>).data.length ? (this.expandedSubElement = this.expandedSubElement === element ? null : element) : null;
this.cd.detectChanges();
this.innerTables.forEach((table, index) => (table.dataSource as MatTableDataSource<Address>).sort = this.innerSort.toArray()[index]);
}
applyFilter(filterValue: string) {
this.innerTables.forEach((table, index) => (table.dataSource as MatTableDataSource<Address>).filter = filterValue.trim().toLowerCase());
}
}
export interface User {
name: string;
email: string;
phone: string;
addresses?: Address[] | MatTableDataSource<Address>;
}
export interface Address {
street: string;
zipCode: string;
city: string;
blocks?: Block[] | MatTableDataSource<Block>;
}
export interface Block {
name: string;
level: string;
unitnumber: string;
}
export interface UserDataSource {
name: string;
email: string;
phone: string;
addresses?: MatTableDataSource<Address>;
}
const USERS: User[] = [
{
name: "Mason",
email: "mason@test.com",
phone: "9864785214",
addresses: [
{
street: "Street 1",
zipCode: "78542",
city: "Kansas",
blocks: [
{
name: "Blk 11",
level: "Lvl 1",
unitnumber: "01-01"
},
{
name: "Blk 22",
level: "Lvl 2",
unitnumber: "02-01"
}
]
},
{
street: "Street 2",
zipCode: "78554",
city: "Texas",
blocks: [
{
name: "Blk 33",
level: "Lvl 3",
unitnumber: "03-02"
},
{
name: "Blk 44",
level: "Lvl 4",
unitnumber: "04-02"
}
]
}
]
},
{
name: "Jason",
email: "jason@test.com",
phone: "7856452187",
addresses: [
{
street: "Street 5",
zipCode: "23547",
city: "Utah"
},
{
street: "Street 5",
zipCode: "23547",
city: "Ohio"
}
]
}
];
/** Copyright 2019 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license */
table-expandable-rows-example.html
<table mat-table #outerSort="matSort" [dataSource]="dataSource" multiTemplateDataRows class="mat-elevation-z8" matSort>
<ng-container matColumnDef="{{column}}" *ngFor="let column of columnsToDisplay">
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{column}} </th>
<td mat-cell *matCellDef="let element"> {{element[column]}} </td>
</ng-container>
<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
<ng-container matColumnDef="expandedDetail">
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplay.length">
<div class="example-element-detail" *ngIf="element.addresses?.data.length" [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
<div class="inner-table mat-elevation-z8" *ngIf="expandedElement">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
<table #innerTables mat-table #innerSort="matSort" [dataSource]="element.addresses" matSort>
<ng-container matColumnDef="{{innerColumn}}" *ngFor="let innerColumn of innerDisplayedColumns">
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{innerColumn}} </th>
<td mat-cell *matCellDef="let element"> {{element[innerColumn]}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="innerDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: innerDisplayedColumns;"></tr>
</table>
</div>
</div>
</td>
</ng-container>
<!-- Expanded SubContent Column - The subdetail row is made up of this one column that spans across all columns -->
<ng-container matColumnDef="expandedSubDetail">
<td mat-cell *matCellDef="let element" [attr.colspan]="innerDisplayedColumns.length">
<div class="example-element-detail" *ngIf="element.blocks?.data.length" [@detailExpand]="element == expandedSubElement ? 'expanded' : 'collapsed'">
<div class="inner-table mat-elevation-z8" *ngIf="expandedSubElement">
<table #innerTables mat-table #innerSort="matSort" [dataSource]="element.addresses" matSort>
<ng-container matColumnDef="{{innerColumn}}" *ngFor="let innerColumn of innerDisplayedColumns">
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{innerColumn}} </th>
<td mat-cell *matCellDef="let element"> {{element[innerColumn]}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="subBlockDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: subBlockDisplayedColumns;"></tr>
</table>
</div>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
<tr mat-row *matRowDef="let element; columns: columnsToDisplay;" [class.example-element-row]="element.addresses?.data.length"
[class.example-expanded-row]="expandedElement === element" (click)="toggleRow(element)">
</tr>
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row" [class.example-expanded-row]="expandedSubElement === element" (click)="toggleSubRow(element)"></tr>
<tr mat-row *matRowDef="let row; columns: ['expandedSubDetail']" class="example-detail-row"></tr>
</table>
<!-- Copyright 2019 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license -->