如何在Angular Material Table中实现LocalDate类型列自定义排序?
解决Angular Material Table中@js-joda/core LocalDate列的排序问题
核心问题分析
问题根源在于MatTableDataSource的sortingDataAccessor签名限制只能返回string | number,无法直接返回LocalDate实例,导致你在sortData中拿到的可能是LocalDate的字符串形式(而非实例),无法调用compareTo方法,最终排序失效或抛出异常。
方案一:重写sortingDataAccessor返回可排序数值(推荐)
直接重写sortingDataAccessor,针对LocalDate类型的列返回其epoch day(从1970-01-01开始的天数,数字类型),既符合签名要求,又能利用数字的自然排序实现LocalDate的正确顺序。
修改你的LocalDateSortableDataSource类:
import { MatTableDataSource, MatTableDataSourcePaginator } from '@angular/material/table'; import { LocalDate, LocalDateTime } from '@js-joda/core'; export class LocalDateSortableDataSource< T, P extends MatTableDataSourcePaginator = MatTableDataSourcePaginator > extends MatTableDataSource<T, P> { constructor(initialData: T[]) { super(initialData); // 重写sortingDataAccessor,处理日期类型 this.sortingDataAccessor = (data: T, sortHeaderId: string) => { const value = (data as any)[sortHeaderId]; // LocalDate返回epoch day(数字) if (value instanceof LocalDate) { return value.toEpochDay(); } // LocalDateTime返回epoch second(数字) else if (value instanceof LocalDateTime) { return value.toEpochSecond(); } // 其他类型保持原有逻辑 return typeof value === 'string' || typeof value === 'number' ? value : String(value); }; // 重写sortData,直接基于数字/字符串排序 this.sortData = (data: T[], sort) => { const active = sort.active; const direction = sort.direction; if (!active || direction === '') { return data; } return data.sort((a, b) => { const valueA = this.sortingDataAccessor(a, active); const valueB = this.sortingDataAccessor(b, active); let comparatorResult = 0; if (valueA != null && valueB != null) { if (valueA > valueB) { comparatorResult = 1; } else if (valueA < valueB) { comparatorResult = -1; } } else if (valueA != null) { comparatorResult = 1; } else if (valueB != null) { comparatorResult = -1; } return comparatorResult * (direction === 'asc' ? 1 : -1); }); }; } }
方案二:在sortData中直接访问原始数据字段
如果不想修改sortingDataAccessor,可以在sortData里直接从原始数据对象中获取LocalDate实例,绕过签名限制:
import { MatTableDataSource, MatTableDataSourcePaginator } from '@angular/material/table'; import { LocalDate, LocalDateTime } from '@js-joda/core'; export class LocalDateSortableDataSource< T, P extends MatTableDataSourcePaginator = MatTableDataSourcePaginator > extends MatTableDataSource<T, P> { constructor(initialData: T[]) { super(initialData); this.sortData = (data: T[], sort) => { const active = sort.active; const direction = sort.direction; if (!active || direction === '') { return data; } return data.sort((a, b) => { // 直接从原始数据取字段,不依赖sortingDataAccessor const valueA = (a as any)[active]; const valueB = (b as any)[active]; let comparatorResult = 0; if (valueA != null && valueB != null) { if (valueA instanceof LocalDate) { comparatorResult = valueA.compareTo(valueB as LocalDate); } else if (valueA instanceof LocalDateTime) { comparatorResult = valueA.compareTo(valueB as LocalDateTime); } else { // 其他类型用字符串比较 const strA = String(valueA); const strB = String(valueB); comparatorResult = strA.localeCompare(strB); } } else if (valueA != null) { comparatorResult = 1; } else if (valueB != null) { comparatorResult = -1; } return comparatorResult * (direction === 'asc' ? 1 : -1); }); }; } }
使用方式
在组件中替换原有的MatTableDataSource即可:
// 假设你的数据类型为MyData,包含LocalDate类型字段 dataSource = new LocalDateSortableDataSource<MyData>(yourDataArray);
内容的提问来源于stack exchange,提问作者Master Qiao
相关产品推荐
相关产品推荐

