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

在独立组件中定义Angular Material表格列并实现组件复用

Angular Material 通用表格的列组件化复用实现

要实现通用表格封装,同时将部分列定义拆分为独立组件复用,核心是解决嵌套组件中MatColumnDef的传递问题——默认@ContentChildren只能捕获当前组件直接投影的内容,无法获取嵌套子组件内部的列定义,需要通过以下步骤调整:

1. 改造列组件,暴露内部列定义

修改my-custom-columns组件,将内部的MatColumnDef实例暴露给父组件:

// my-custom-columns.component.ts
import { Component, ViewChildren, QueryList } from '@angular/core';
import { MatColumnDef } from '@angular/material/table';

@Component({
  selector: 'my-custom-columns',
  templateUrl: './my-custom-columns.html',
})
export class MyCustomColumnsComponent {
  // 若组件内有多个列定义,用@ViewChildren获取全部
  @ViewChildren(MatColumnDef) columnDefs!: QueryList<MatColumnDef>;
}

组件模板保持不变:

<!-- my-custom-columns.html -->
<ng-container matColumnDef="id">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>ID</th>
    <td mat-cell *matCellDef="let row">#{{ row.id | number }}</td>
</ng-container>

2. 更新通用表格组件,收集嵌套列定义

在wrapper-table中,新增对MyCustomColumnsComponent实例的捕获,遍历提取内部的列定义并添加到表格:

// wrapper-table.component.ts
import { Component, Input, ViewChild, ContentChildren, QueryList, AfterContentInit } from '@angular/core';
import { MatTable, MatColumnDef, MatHeaderRowDef, MatRowDef, MatNoDataRow, DataSource } from '@angular/material/table';
import { MyCustomColumnsComponent } from './my-custom-columns.component';

@Component({
  selector: 'wrapper-table',
  templateUrl: './wrapper-table.html',
})
export class WrapperTable<T> implements AfterContentInit {
  @ContentChildren(MatHeaderRowDef) headerRowDefs!: QueryList<MatHeaderRowDef>;
  @ContentChildren(MatRowDef) rowDefs!: QueryList<MatRowDef<T>>;
  @ContentChildren(MatColumnDef) columnDefs!: QueryList<MatColumnDef>;
  // 捕获所有嵌套的自定义列组件
  @ContentChildren(MyCustomColumnsComponent) customColumnComponents!: QueryList<MyCustomColumnsComponent>;
  @ContentChild(MatNoDataRow) noDataRow!: MatNoDataRow;

  @ViewChild(MatTable, {static: true}) table!: MatTable<T>;

  @Input() columns!: string[];
  @Input() dataSource!: DataSource<T>;

  ngAfterContentInit() {
    // 添加直接投影的列定义
    this.columnDefs.forEach(def => this.table.addColumnDef(def));
    // 添加嵌套组件中的列定义
    this.customColumnComponents.forEach(component => {
      component.columnDefs.forEach(def => this.table.addColumnDef(def));
    });

    this.rowDefs.forEach(def => this.table.addRowDef(def));
    this.headerRowDefs.forEach(def => this.table.addHeaderRowDef(def));
    this.table.setNoDataRow(this.noDataRow);
  }
}

3. 在业务组件中使用自定义列

确保displayedColumns包含自定义列的名称,正常引入列组件即可:

// my-component.component.ts
import { Component } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';

interface UserData {
  position: number;
  name: string;
  id: number;
}

@Component({
  selector: 'my-component',
  templateUrl: './my-component.html',
})
export class MyComponent {
  displayedColumns = ['position', 'name', 'id']; // 包含自定义的id列
  dataSource = new MatTableDataSource<UserData>([
    {position: 1, name: '张三', id: 1001},
    {position: 2, name: '李四', id: 1002},
  ]);
}
<!-- my-component.html -->
<wrapper-table [dataSource]="dataSource" [columns]="displayedColumns" matSort #sort="matSort">
  <!-- 直接定义的列 -->
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef> 姓名 </th>
    <td mat-cell *matCellDef="let element"> {{element.name}} </td>
  </ng-container>

  <!-- 引入独立的自定义列组件 -->
  <my-custom-columns></my-custom-columns>

  <!-- 行定义 -->
  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

  <!-- 无数据行 -->
  <tr class="mat-row" *matNoDataRow>
    <td class="mat-cell" colspan="3">暂无数据</td>
  </tr>
</wrapper-table>

注意事项

  • 自定义列组件中matColumnDef的名称必须与displayedColumns数组中的值完全匹配
  • 若需要支持多个不同类型的自定义列组件,可通过抽象类或接口统一规范,避免在wrapper-table中引入过多具体组件依赖
  • 若列组件需要支持排序、筛选等交互,确保对应的指令(如mat-sort-header)正确绑定到表格的matSort实例

内容的提问来源于stack exchange,提问作者mar.io

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 22:15:30