You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何动态生成并展示任意层级嵌套的Angular Material Table

实现方案

核心逻辑是通过递归组件实现任意层级的嵌套表格,无需硬编码层级,只要行数据存在子项就可以自动渲染下一级表格。


1. 封装可递归的嵌套表格组件

创建独立的nested-table组件,组件内部可以调用自身,实现层级无限嵌套:

  • 组件入参定义:接收dataSource(当前层级的数据源)、displayedColumns(当前层级要展示的列)两个输入属性即可
  • 组件内部直接复用你现有的表格展开逻辑,内层再次调用自身组件渲染下一级

2. 改造行点击懒加载逻辑

你不需要更新整体数据源,因为每一行的element是引用类型,直接给当前点击的element追加_mdbpartchilds属性即可,Angular变更检测会自动同步到绑定的子表格数据源上:

// 写在递归组件的ts文件中
toggleRow(element: any) {
  // 如果已经加载过子数据,直接切换展开状态
  if (element._mdbpartchilds) {
    element.isExpanded = !element.isExpanded;
    return;
  }
  // 没加载过则调用后端接口拉取子数据
  this.yourApiService.getChildData(element.id).subscribe(res => {
    // 直接给当前行对象挂载子数据源,无需修改上层整体数据源
    element._mdbpartchilds = res;
    element.isExpanded = true;
  })
}

3. 递归组件模板示例

你现有一级嵌套的代码可以直接复用,只需要把内层的固定表格替换成递归调用自己的组件即可:

<!-- nested-table.component.html 递归组件模板 -->
<table mat-table [dataSource]="dataSource" matSort>
  <!-- 普通列定义 -->
  <ng-container matColumnDef="{{column}}" *ngFor="let column of displayedColumns">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> {{column}} </th>
    <td mat-cell *matCellDef="let element"> {{element[column]}} </td>
  </ng-container>

  <!-- 展开详情列定义 -->
  <ng-container matColumnDef="expandedDetail">
    <td mat-cell *matCellDef="let element" [attr.colspan]="displayedColumns.length">
      <div class="example-element-detail" *ngIf="element._mdbpartchilds?.length" [@detailExpand]="element.isExpanded ? 'expanded' : 'collapsed'">
        <div class="inner-table mat-elevation-z8">
          <!-- 递归调用自身组件,传入子级数据源和列配置 -->
          <nested-table 
            [dataSource]="element._mdbpartchilds" 
            [displayedColumns]="displayedColumns"
          ></nested-table>
        </div>
      </div>
    </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let element; columns: displayedColumns;" 
    [class.example-element-row]="element._mdbpartchilds?.length || element.hasChildren"
    [class.example-expanded-row]="element.isExpanded" 
    (click)="toggleRow(element)">
  </tr>
  <tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>
</table>

4. 父组件调用

最外层直接调用一次递归组件传入根数据源即可:

<!-- 父组件模板 -->
<nested-table 
  [dataSource]="rootDataSource" 
  [displayedColumns]="rootDisplayedColumns"
></nested-table>

可选优化点

  • 如果不同层级的展示列不一样,可以给递归组件新增level输入属性,根据层级切换不同的列配置
  • 如果要提前判断某行是否有子项不需要先加载,可以给行数据加hasChildren标记,点击时再加载子数据,避免无意义的接口请求
  • 展开状态如果要支持全局控制,可以用全局Map存储每行的展开状态,不需要挂载到element对象上

内容的提问来源于stack exchange,提问作者Pravin Ambekar.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 16:36:03