在独立组件中定义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
相关产品推荐
相关产品推荐

