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

Angular Material表格编程触发排序异常问题求助

问题:Angular Material表格按日期排序仅按日部分排序,而非完整日期

我开发了一个基于Angular Material框架的演示应用,用于对ScheduleDateTime类型的数据数组进行排序。问题在于,当通过代码this.sort.sort(({ id: 'date', start: 'asc' }) as MatSortable);触发排序时,结果不符合预期:本该按完整日期(DD-MM-yyyy格式)排序,但实际仅按日期的日(DD)部分排序。


app.component.ts代码

import { AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort, MatSortable, Sort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import * as moment from 'moment';
import { timer } from 'rxjs';

export interface SortableDate {
  date: string
}
export class ScheduleDateTime {
  id!: string;
  date!: string;
  highlighted! : boolean;
}
const COLUMNS_SCHEMA = [
  {
    key: "date",
    type: "text",
    label: "Date"
  },
]
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterViewInit {
  @ViewChild('paginator') paginator!: MatPaginator;
  @ViewChild(MatSort) sort!: MatSort;
  title = 'TableTest';
  displayedColumns: string[] = COLUMNS_SCHEMA.map((col) => col.key);
  columnsSchema: any = COLUMNS_SCHEMA;
  dataSource!: MatTableDataSource<ScheduleDateTime>;
  futureScheduleDate: ScheduleDateTime[] = [];
  constructor(
    private changeDetector: ChangeDetectorRef
  ) { }
  ngOnInit(): void {
    this.dataSource = new MatTableDataSource();

  }
  ngAfterViewInit(): void {
    timer(0).subscribe(() => {
      this.futureScheduleDate = this.getTestData();
      this.dataSource.data = this.futureScheduleDate;
      this.dataSource.paginator = this.paginator;
      this.dataSource.sort = this.sort;

      // Initial sorting by date
      this.sort.sort(({ id: 'date', start: 'asc' }) as MatSortable);

    })
  }
  sortData(sort: Sort) {
    AppComponent.sortMethod(this.futureScheduleDate, sort);
    this.dataSource.data = this.futureScheduleDate;
    this.changeDetector.detectChanges();
  }
  getTestData(): ScheduleDateTime[] {
    var retVal =
      [
        {
          id: '',
          date: "24-03-1961",
          highlighted: false
        },
        {
          id: '',
          date: "25-03-1961",
          highlighted: false
        },
        {
          id: '',
          date: "26-03-1961",
          highlighted: false
        },
        {
          id: '',
          date: "27-03-1961",
          highlighted: false
        },
        {
          id: '',
          date: "24-04-1961",
          highlighted: false
        },
        {
          id: '',
          date: "25-04-1961",
          highlighted: false
        },
        {
          id: '',
          date: "26-04-1961",
          highlighted: false
        },
        {
          id: '',
          date: "27-04-1961",
          highlighted: false
        }
      ];
    return retVal;
  }
  static sortMethod(dateSort: SortableDate[], sort: Sort) {


    if (!sort.active || sort.direction == '') {
      return;
    }
    console.log(sort);
    dateSort.sort((a, b) => {
      let isAsc = sort.direction == 'asc';

      var date1 = moment(a.date, 'DD-MM-yyyy HH:mm').toDate().getTime();
      var date2 = moment(b.date, 'DD-MM-yyyy HH:mm').toDate().getTime();

      if (date1 < date2) {
        console.log("date1 is earlier than date2");
        return isAsc ? -1 : 1;
      } else if (date1 > date2) {
        console.log("date1 is later than date2");
        return isAsc ? 1 : -1;;
      } else {
        console.log("date1 and date2 are the same");
        return 0;
      }
    });

  }

}

app.component.html代码

<mat-table #table [dataSource]="dataSource" matSort (matSortChange)="sortData($event)" style="min-width: 800px;">
  <ng-container matColumnDef="date">
    <mat-header-cell *matHeaderCellDef mat-sort-header> Date </mat-header-cell>
    <mat-cell *matCellDef="let schedule">
      <input type="text" matInput [value]="schedule.date" readonly>
    </mat-cell>
  </ng-container>
  <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: displayedColumns;let i = index;"
    [ngClass]="{hovered: row.hovered, highlighted: row.highlighted}" #tr (mouseover)="row.hovered = true"
    (mouseout)="row.hovered = false"></mat-row>
</mat-table>
<mat-paginator #paginator pageSize='10' [length]="futureScheduleDate.length" [pageSizeOptions]="[1, 30, 50, 100]"
  showFirstLastButtons>
</mat-paginator>

问题原因与修复方案

原因分析

问题出在初始排序时,Angular Material的MatSort默认使用字符串排序,而非你自定义的sortMethod。当你调用this.sort.sort(...)时,这个操作会触发MatSort的默认排序逻辑,而不是你的sortData方法——只有点击表头的排序按钮时,才会触发(matSortChange)事件并调用sortData。

另外,你的moment解析格式也有问题:测试数据里的日期只有DD-MM-yyyy,但你用了DD-MM-yyyy HH:mm来解析,虽然moment会兼容,但最好保持格式一致。

修复步骤

1. 设置自定义排序访问器,替换默认逻辑

通过dataSource.sortingDataAccessor可以指定每个字段的排序依据,这样无论是初始调用排序还是点击表头排序,都会使用你的规则:

在ngAfterViewInit中添加以下代码:

this.dataSource.sortingDataAccessor = (item, property) => {
  if (property === 'date') {
    // 将DD-MM-yyyy格式的字符串转为时间戳,用于数值排序
    return moment(item.date, 'DD-MM-yyyy').valueOf();
  }
  return item[property as keyof ScheduleDateTime];
};

2. 简化代码(可选)

设置sortingDataAccessor后,MatTable会自动处理排序逻辑,你可以移除手动的sortData方法和模板中的(matSortChange)绑定:

  • 删除组件中的sortData方法
  • 修改模板,移除(matSortChange)="sortData($event)":
<mat-table #table [dataSource]="dataSource" matSort style="min-width: 800px;">
  <!-- 其余内容不变 -->
</mat-table>

3. 修正moment解析格式

将日期解析格式改为和测试数据一致的DD-MM-yyyy,避免不必要的兼容处理。

完整修改后的ngAfterViewInit示例

ngAfterViewInit(): void {
  timer(0).subscribe(() => {
    this.futureScheduleDate = this.getTestData();
    this.dataSource.data = this.futureScheduleDate;
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;

    // 设置自定义排序访问器
    this.dataSource.sortingDataAccessor = (item, property) => {
      if (property === 'date') {
        return moment(item.date, 'DD-MM-yyyy').valueOf();
      }
      return item[property as keyof ScheduleDateTime];
    };

    // 初始排序
    this.sort.sort(({ id: 'date', start: 'asc' }) as MatSortable);
  })
}

为什么之前只按日排序?

当MatSort默认处理字符串排序时,会逐字符比较日期字符串:比如"24-04-1961"和"25-03-1961",前两位字符24小于25,所以会把24-04-1961排在25-03-1961前面,完全忽略了后面的月份和年份,导致看起来只按日排序。

通过sortingDataAccessor将日期转为时间戳后,排序时会按照实际的日期大小比较,而非字符串顺序。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 21:37:04