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

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):

  1. @ViewChild(MatSort)只会获取DOM中第一个MatSort组件实例,后续面板的表格无法关联到自身的排序组件;
  2. 全局的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:45:33