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

Angular Mat-Table分组视图下数据排序失效问题求助

Material Table分组视图排序异常问题

我参照视频实现了Material Table的分组视图,但无法正确排序。期望效果是:

**年份**
  春季的所有活动
  夏季的所有活动
  秋季的所有活动
  冬季的所有活动
**年份**
  春季的所有活动
  夏季的所有活动
  秋季的所有活动
  冬季的所有活动

每个季节对应activitySeasonID(春季1、夏季2、秋季3、冬季4),希望以此字段排序,但分组项没有该ID,导致排序后所有分组项被顶到最前面,无法保持分组结构并对组内条目排序。


HTML代码

<mat-table #table [dataSource]="dataSource" matSort>

    <ng-container matColumnDef="activitySeasonID">
        <mat-header-cell *matHeaderCellDef class="bold" mat-sort-header></mat-header-cell>
        <mat-cell *matCellDef="let activity" class="field-text"> {{ activity?.activitySeasonID }} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="activitySeason">
        <mat-header-cell *matHeaderCellDef class="bold"> 季节 </mat-header-cell>
        <mat-cell *matCellDef="let activity" class="field-text" [ngStyle]="{'color':seasonColour(activity.activitySeason)}"> {{ activity?.activitySeason }} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="character">
        <mat-header-cell *matHeaderCellDef class="bold"> 角色 </mat-header-cell>
        <mat-cell *matCellDef="let activity" class="field-text"> {{ activity?.character }} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="activity">
        <mat-header-cell *matHeaderCellDef class="bold"> 活动 </mat-header-cell>
        <mat-cell *matCellDef="let activity" class="field-text"> {{ activity?.activity }} </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"  mat-row-group></mat-row>
    
    <ng-container matColumnDef="groupHeader">
        <mat-cell *matCellDef="let groupBy" class="bold" style="color: #ffffff; background-color: #011f43">{{groupBy.activityYear}}</mat-cell>
    </ng-container>
    <mat-row *matRowDef="let row; columns: ['groupHeader']; when: isGroup"></mat-row>

</mat-table>

TS代码

const groupBy = function (xs, key) {
  return xs.reduce(function (rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x);
    return rv;
  }, {});
}

export class CovenantActivitiesComponent implements OnInit {

  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;
  displayedColumns = ['activitySeason', 'character', 'activity'];
  dataSource: MatTableDataSource<CovenantActivities>;
  activityData = [];

// ....构造函数及其他无关代码

ngOnInit(): void {
this.covenantDataService.getCovenantSeasonalActivites(this.covID).subscribe(data => {
   let tempdatasource = []
   this.activityData = groupBy(data, 'activityYear');

   Object.keys(this.activityData).forEach(key => {
     tempdatasource.push({activityYear: key, isGroupBy: true });
     let values = this.activityData[key];
     values.forEach((element) => {tempdatasource.push(element);});
   });

   this.dataSource = new MatTableDataSource<CovenantActivities>(tempdatasource)
   this.sort.sort(({ id: 'activitySeasonID', start: 'asc'}) as MatSortable);
   this.dataSource.sort = this.sort;      
  });
}

isGroup (index, item): boolean {
  return item.isGroupBy;
}

效果问题

  • 分组正确但未排序的效果:
    分组正确但未排序

  • 添加排序后出现的问题:
    添加排序后的问题

数据源示例

0: {activityYear: '1220', isGroupBy: true}
1: {activityYear: '1220', activitySeason: 'Summer', activityID: 'jSJuQXp2OchWDXlKAWTS', activity: 'wefsdfsdfsdfsfs', character: 'Britta', …}
2: {activityYear: '1222', isGroupBy: true}
3: {activitySeasonID: 4, activity: 'f fsdf sdfds fsdf sdfsdfs', activitySeason: 'Winter', activityYear: '1222', character: 'Theren', …}
4: {activityYear: '1229', isGroupBy: true}
5: {activity: 'sdfs fsdfsdfsdfsdfsdfsd', activitySeason: 'Winter', activityID: '31nmWjZbix6xlytUXeCA', character: 'Sidorov', activitySeasonID: 4, …}
6: {activity: 'das dasd asd dasd asd asds', character: 'Belisarius', activitySeason: 'Winter', activityID: 'm9AQUecuwhIms7WIslQp', …}
7: {activityYear: '1230', isGroupBy: true}
8: {activity: 'dfs fsdfsdf sdfdsfsdf', activityID: '6X6kAJgakXdfOaXqqN1o', activitySeason: 'Summer', activitySeasonID: 2, activityYear: '1230', …}
9: {activitySeason: 'Spring', activitySeasonID: 1, activity: 'Stuff was done', activityID: 'OMbZzNSqdcuO2uZf9oC6', …}
10: {activityYear: '1230', activityID: 'QTWGNDMzZ01LmTBU1ZaJ', activitySeasonID: 1, character: 'Theren', activitySeason: 'Spring', …}
11: {activityYear: '1233', isGroupBy: true}
12: {activityID: '2WIe1UsWkVHDEQzx9VWp', activitySeason: 'Autumn', activity: ' fsd fsdf sdf sdf sdfsd fsdf', activityYear: '1233', character: 'Theren', …}
13: {activityYear: '1233', character: 'Albertus Magnus', activityID: '3Yfwi7pDDdYjafRBWzc3', activitySeason: 'Summer', activity: 'dsfsd fsdfsdfsdf', …}
14: {activity: 'dfsf fsdfsdfsdfs dsf', activityID: '4EkzaSTwgPAPF7nKU3Sr', activitySeasonID: 2, character: 'Sargon', activityYear: '1233', …}
15: {character: 'Britta', activity: 'sdfdfsdf', activitySeasonID: 3, activitySeason: 'Autumn', activityYear: '1233', …}
16: {activityYear: '1240', isGroupBy: true}
17: {activity: 'sdfsdf dsfsdf sdfsd', activityID: 'ck2bhNb2E1Sgo71RZniJ', activitySeason: 'Summer', character: 'Sargon', activitySeasonID: 2, …}

解决方案

问题根源是分组行没有activitySeasonID字段,全局排序会打乱分组结构。要保持分组结构并对组内条目排序,有两种处理方式:

方式1:提前对组内数据排序

直接在构建数据源时,对每个年份下的活动按activitySeasonID排序,不需要依赖MatTable的全局排序:

ngOnInit(): void {
this.covenantDataService.getCovenantSeasonalActivites(this.covID).subscribe(data => {
   let tempdatasource = []
   this.activityData = groupBy(data, 'activityYear');

   Object.keys(this.activityData).forEach(key => {
     tempdatasource.push({activityYear: key, isGroupBy: true });
     let values = this.activityData[key];
     // 对当前年份下的活动按activitySeasonID升序排序
     values.sort((a, b) => (a.activitySeasonID || 0) - (b.activitySeasonID || 0));
     values.forEach((element) => {tempdatasource.push(element);});
   });

   this.dataSource = new MatTableDataSource<CovenantActivities>(tempdatasource);
  });
}

方式2:支持表头切换排序(保持分组结构)

如果需要点击表头切换排序方向,同时保证分组行始终在对应组顶部,自定义排序访问器:

ngOnInit(): void {
this.covenantDataService.getCovenantSeasonalActivites(this.covID).subscribe(data => {
   let tempdatasource = []
   this.activityData = groupBy(data, 'activityYear');

   Object.keys(this.activityData).forEach(key => {
     tempdatasource.push({activityYear: key, isGroupBy: true });
     let values = this.activityData[key];
     values.forEach((element) => {tempdatasource.push(element);});
   });

   this.dataSource = new MatTableDataSource<CovenantActivities>(tempdatasource);
   // 自定义排序逻辑:分组行始终排在组最前
   this.dataSource.sortingDataAccessor = (item, property) => {
     if (item.isGroupBy) {
       return -Infinity;
     }
     return item[property];
   };
   this.dataSource.sort = this.sort;
   this.sort.sort(({ id: 'activitySeasonID', start: 'asc'}) as MatSortable);
  });
}

两种方式都能解决排序打乱分组的问题,方式2额外支持交互式排序操作。

内容的提问来源于stack exchange,提问作者Dusk Childer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 11:01:36