如何用Mat-table指令实现含可变数量子行的表格?
用Angular Material Table实现可变子行表格
核心思路
不需要创建大量行模板,而是先扁平化数据源,将每个学生的主行和对应的多条报告子行整合为一个统一的数组,给每个条目标记类型(主行/子行),再通过multiTemplateDataRows和多套*matRowDef来渲染不同类型的行。
步骤1:定义整合后的数据源类型
先扩展数据结构,用来区分主行和子行:
// 整合后的行数据类型 type TableRow = | { type: 'header', student: string } | { type: 'report', report: Report }; // 原数据接口不变 interface ReportCard { student: string; reports: Report[]; } interface Report { class: string; teacher: string; grade: string; }
步骤2:扁平化原数据源
在组件中把原始的ReportCard数组转换为TableRow数组:
import { Component } from '@angular/core'; @Component({ selector: 'table-basic-example', templateUrl: 'table-basic-example.html', styleUrls: ['table-basic-example.css'], }) export class TableBasicExample { // 原始示例数据 originalData: ReportCard[] = [ { student: '张三', reports: [ { class: '数学', teacher: '王老师', grade: 'A' }, { class: '英语', teacher: '李老师', grade: 'B+' } ] }, { student: '李四', reports: [ { class: '语文', teacher: '张老师', grade: 'A-' } ] } ]; // 扁平化后的表格数据源 dataSource: TableRow[] = []; // 表格列定义 displayedColumns = ['student', 'class', 'teacher', 'grade']; ngOnInit() { this.flattenData(); } private flattenData() { this.originalData.forEach(card => { // 添加主行 this.dataSource.push({ type: 'header', student: card.student }); // 添加该学生的所有报告子行 card.reports.forEach(report => { this.dataSource.push({ type: 'report', report }); }); }); } }
步骤3:编写表格模板
利用multiTemplateDataRows启用多模板行渲染,针对不同type配置对应的*matRowDef:
<table mat-table [dataSource]="dataSource" multiTemplateDataRows class="mat-elevation-z8"> <!-- 学生名列 --> <ng-container matColumnDef="student"> <th mat-header-cell *matHeaderCellDef> 学生 </th> <td mat-cell *matCellDef="let row" [attr.colspan]="row.type === 'header' ? 4 : 1"> {{ row.type === 'header' ? row.student : '' }} </td> </ng-container> <!-- 课程列 --> <ng-container matColumnDef="class"> <th mat-header-cell *matHeaderCellDef> 课程 </th> <td mat-cell *matCellDef="let row"> {{ row.type === 'report' ? row.report.class : '' }} </td> </ng-container> <!-- 老师列 --> <ng-container matColumnDef="teacher"> <th mat-header-cell *matHeaderCellDef> 老师 </th> <td mat-cell *matCellDef="let row"> {{ row.type === 'report' ? row.report.teacher : '' }} </td> </ng-container> <!-- 成绩列 --> <ng-container matColumnDef="grade"> <th mat-header-cell *matHeaderCellDef> 成绩 </th> <td mat-cell *matCellDef="let row"> {{ row.type === 'report' ? row.report.grade : '' }} </td> </ng-container> <!-- 主行模板 --> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;" [class.header-row]="row.type === 'header'"></tr> </table>
步骤4:添加样式优化(可选)
给主行加样式区分:
.header-row { background-color: #f5f5f5; font-weight: bold; }
关键说明
- 通过扁平化数据,避免了创建大量模板,不管每个学生有多少报告,都只用两套行逻辑处理。
- 主行通过
colspan="4"占据所有列,子行正常填充对应列数据。 - 利用
row.type判断当前行类型,渲染对应内容。
内容的提问来源于stack exchange,提问作者ahong
相关产品推荐
相关产品推荐

