Angular MatSort搭配动态列mat-table点击排序无响应问题
问题描述
mat-table 支持动态新增、删除列,已为各列配置mat-sort-header排序头,预期点击列头可对表格数据排序,但实际点击排序头后无任何响应。
实现代码
<ng-container *ngFor="let column of columns"> <ng-container [matColumnDef]="column" *ngIf="column !== 'selection'"> <th class="cell-header" mat-header-cell mat-sort-header *matHeaderCellDef> {{column}} </th> <td class="template-cell" mat-cell *matCellDef="let element"> {{element[column]}} </ng-container> </ng-container>
已确认信息
- 列通过遍历
string[]类型的columns数组动态渲染,columns数组会不定期更新 - 每次列配置更新时,都会重新执行以下逻辑,认为基础配置不存在问题:
this.dataSource = new MatTableDataSource(data) this.dataSource.sort = this.sort
- 已排查字段路径包含
.分隔符导致的排序异常场景,相关解决方案不适用于当前场景 - 已知
matColumnDef取值需要和matCell中绑定的数据字段名保持一致,但动态列场景下不确定正确配置方式,疑问是否需要编写自定义排序函数才能实现动态列排序功能。
问题原因
动态列更新时,新的mat-sort-header是在列配置变更后才完成视图渲染,提前赋值的MatSort实例无法捕获到后渲染的排序头实例,导致排序事件未绑定;同时模板中td标签未闭合,也可能引发指令初始化异常。该场景不需要编写全局自定义排序函数即可实现排序功能。
修复步骤
- 修复模板语法问题,补全缺失的
td闭合标签:
<td class="template-cell" mat-cell *matCellDef="let element"> {{element[column]}} </td>
- 避免每次列更新都重新实例化
MatTableDataSource,仅更新dataSource.data属性即可,减少重复实例化导致的指令绑定丢失 - 获取
MatSort实例时配置{static: false},列配置更新后先触发变更检测,待动态列头完成渲染后再重新绑定sort实例,参考代码如下:
import { MatSort } from '@angular/material/sort'; import { ChangeDetectorRef } from '@angular/core'; @ViewChild(MatSort, {static: false}) sort!: MatSort; constructor(private cdr: ChangeDetectorRef) {} // 列更新逻辑 updateTable(newColumns: string[], tableData: any[]) { this.columns = newColumns; this.dataSource.data = tableData; // 触发变更检测,完成动态列渲染 this.cdr.detectChanges(); // 重新绑定sort实例,注册所有新生成的排序头 this.dataSource.sort = this.sort; }
- 若后续存在动态列展示名和实际数据字段名不一致的场景,直接给
mat-sort-header传入对应数据字段名即可,示例:<th mat-sort-header="实际数据字段名">列展示名</th>,无需额外编写全局自定义排序。
内容的提问来源于stack exchange,提问作者Pascal van der Donk
相关产品推荐
相关产品推荐

