Accordion嵌套Material Table:第二个面板表格排序失效求助
问题描述
我用Angular Material实现了多个折叠面板(Accordion),每个面板内包含独立的Material Table。点击第一个面板时,能正常调用API获取数据并渲染表格,排序功能正常;但打开第二个面板时,内部表格的排序完全失效。
HTML代码
<mat-accordion multi="false" [togglePosition]="'before'"> <ng-container *ngFor="let item of usersData; let i=index;last as last"> <mat-expansion-panel class="mt-2" #isExpanded> <mat-expansion-panel-header (click)="showData(item.id, isExpanded.expanded)"> <table> <tr> <th class="col-4 px-2">Number</th> <th class="col-4">Name</th> <th class="col-4">Date</th> <td> <button mat-icon-button matTooltip="Delete"> <mat-icon class="mat-icon mat-primary">delete</mat-icon> </button> </td> </tr> <tr> <td class="col-4 px-2"><span matTooltip="{{item.number}}">{{item.number}}</span></td> <td class="col-4"><span matTooltip="{{item.name}}">{{item.name}}</span></td> <td class="col-4"><span matTooltip="{{item.createdAt | date: 'yyyy-MM-dd'}}">{{item.createdAt | date: 'yyyy-MM-dd'}}</span></td> <td></td> </tr> </table> </mat-expansion-panel-header> <div class="m-2" fxLayout="row wrap" fxLayoutGap="20px"> <div class="bh-table-title bh-table mt-2 scroll"> <table mat-table *ngIf="dataSource.data.length > 0" [dataSource]="dataSource" matSort #MatSorteds matSortStart="desc"> <ng-container matColumnDef="number"> <th mat-header-cell *matHeaderCellDef mat-sort-header>Number</th> <td mat-cell *matCellDef="let element" matTooltip="{{element.number}}"> <button class="link" (click)="navigate(element)">{{element.number}}</button> </td> </ng-container> <ng-container matColumnDef="Name"> <th mat-header-cell *matHeaderCellDef mat-sort-header> Name</th> <td mat-cell *matCellDef="let element" matTooltip="{{element.Name}}"> {{element.name}}</td> </ng-container> <ng-container matColumnDef="createdAt"> <th mat-header-cell *matHeaderCellDef mat-sort-header>Date</th> <td mat-cell *matCellDef="let element" matTooltip="{{element.createdAt| date: 'yyyy-MM-dd'}}"> {{element.createdAt | date: 'yyyy-MM-dd'}}</td> </ng-container> <tr mat-header-row *matHeaderRowDef="columnsToDisplay;sticky: true" mat-sort></tr> <tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr> </table> </div> </div> <div class="no-records-panel" *ngIf="dataSource.data.length === 0">No Records found</div> </mat-expansion-panel> </ng-container> </mat-accordion>
组件代码
@ViewChild(MatSort) matSort!: MatSort; ngAfterViewInit(): void { this.dataSource.data = this.data; this.dataSource.paginator = this.paginator; this.dataSource.sort = this.matSort; }
问题分析
核心原因是所有表格共用了同一个数据源(dataSource)和排序实例(matSort):
@ViewChild(MatSort)只会获取DOM中第一个MatSort组件实例,后续面板的表格无法关联到自身的排序组件;- 全局的
dataSource被所有面板共享,导致数据和排序状态混乱,第二个面板的排序事件无法触发正确的数据更新。
解决方案
1. 为每个面板维护独立的数据源和排序实例
在组件中定义一个映射对象,存储每个面板对应的数据源、排序组件:
import { MatTableDataSource } from '@angular/material/table'; import { MatSort } from '@angular/material/sort'; // 定义每个面板的存储结构 interface PanelContext { dataSource: MatTableDataSource<any>; sort?: MatSort; } // 用item.id作为key,存储对应面板的上下文 panelContextMap: { [key: string]: PanelContext } = {};
2. 修改HTML模板,绑定独立的数据源和排序
为循环中的每个表格绑定对应面板的数据源,并通过模板引用传递自身的MatSort:
<mat-accordion multi="false" [togglePosition]="'before'"> <ng-container *ngFor="let item of usersData"> <mat-expansion-panel class="mt-2"> <mat-expansion-panel-header (click)="showData(item.id)"> <!-- 头部表格内容保持不变 --> <table> <tr> <th class="col-4 px-2">Number</th> <th class="col-4">Name</th> <th class="col-4">Date</th> <td> <button mat-icon-button matTooltip="Delete"> <mat-icon class="mat-icon mat-primary">delete</mat-icon> </button> </td> </tr> <tr> <td class="col-4 px-2"><span matTooltip="{{item.number}}">{{item.number}}</span></td> <td class="col-4"><span matTooltip="{{item.name}}">{{item.name}}</span></td> <td class="col-4"><span matTooltip="{{item.createdAt | date: 'yyyy-MM-dd'}}">{{item.createdAt | date: 'yyyy-MM-dd'}}</span></td> <td></td> </tr> </table> </mat-expansion-panel-header> <div class="m-2" fxLayout="row wrap" fxLayoutGap="20px"> <div class="bh-table-title bh-table mt-2 scroll"> <!-- 绑定当前面板的数据源,获取自身的MatSort --> <table mat-table *ngIf="panelContextMap[item.id]?.dataSource.data.length > 0" [dataSource]="panelContextMap[item.id].dataSource" matSort #currentSort="matSort" matSortStart="desc" (matSortChange)="bindSortToDataSource(item.id, currentSort)"> <!-- 列定义保持不变 --> <ng-container matColumnDef="number"> <th mat-header-cell *matHeaderCellDef mat-sort-header>Number</th> <td mat-cell *matCellDef="let element" matTooltip="{{element.number}}"> <button class="link" (click)="navigate(element)">{{element.number}}</button> </td> </ng-container> <ng-container matColumnDef="Name"> <th mat-header-cell *matHeaderCellDef mat-sort-header> Name</th> <td mat-cell *matCellDef="let element" matTooltip="{{element.Name}}"> {{element.name}}</td> </ng-container> <ng-container matColumnDef="createdAt"> <th mat-header-cell *matHeaderCellDef mat-sort-header>Date</th> <td mat-cell *matCellDef="let element" matTooltip="{{element.createdAt| date: 'yyyy-MM-dd'}}"> {{element.createdAt | date: 'yyyy-MM-dd'}}</td> </ng-container> <tr mat-header-row *matHeaderRowDef="columnsToDisplay;sticky: true"></tr> <tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr> </table> </div> </div> <div class="no-records-panel" *ngIf="panelContextMap[item.id]?.dataSource.data.length === 0">No Records found</div> </mat-expansion-panel> </ng-container> </mat-accordion>
3. 更新组件逻辑,初始化独立数据源并绑定排序
修改showData方法创建独立数据源,新增bindSortToDataSource方法绑定当前面板的排序组件:
// 面板展开时加载数据并初始化数据源 showData(itemId: string): void { if (!this.panelContextMap[itemId]) { // 调用API获取当前item对应的数据 this.yourApiService.fetchData(itemId).subscribe(data => { const dataSource = new MatTableDataSource(data); this.panelContextMap[itemId] = { dataSource }; }); } } // 将当前表格的排序组件绑定到对应数据源 bindSortToDataSource(itemId: string, sort: MatSort): void { const context = this.panelContextMap[itemId]; if (context) { context.dataSource.sort = sort; // 可选:设置默认排序规则 sort.active = 'createdAt'; sort.direction = 'desc'; context.dataSource.sort.sort({id: 'createdAt', start: 'desc', disableClear: false}); } }
额外注意事项
- 删除原组件中的全局
dataSource和matSort变量,避免冲突; - 如果需要分页功能,需为每个面板创建独立的
MatPaginator实例,逻辑与排序一致; - 组件销毁时可清空
panelContextMap,避免内存泄漏。
内容的提问来源于stack exchange,提问作者sherlok22
相关产品推荐
相关产品推荐

