Angular Material 5.0.3表格数字与文本混合列排序异常问题
解决Angular Material 5.x表格混合数字文本列排序异常的问题
嘿,我之前也碰到过一模一样的问题!Angular Material 5.x的表格默认排序是按字符串的ASCII码值来比较的,不管内容里有没有数字,全当纯字符串处理——这就导致像'12'会排在'1tesxt'后面,纯文本和数字混排顺序完全不符合预期,比如你给出的排序结果'XXX', '1', '1tesxt', '1', 'OPD', 'OXD', '12'就是典型的默认字符串排序的结果。
要解决这个问题,我们需要给表格数据源绑定自定义排序逻辑,让它能区分纯数字、数字开头的文本和纯文本,按照我们期望的规则排序。下面是具体的实现步骤:
1. 定义自定义排序函数
在你的组件类里,先写两个核心函数:一个用来提取排序用的有效值,另一个用来实现具体的排序对比逻辑。
import { MatSort } from '@angular/material'; // 替换成你自己的数据模型类型 interface YourDataModel { model: string; // 其他字段... } // ...组件类内部... // 自定义排序对比逻辑 customSortData(a: YourDataModel, b: YourDataModel, sortHeaderId: string): number { const valueA = this.extractSortValue(a[sortHeaderId]); const valueB = this.extractSortValue(b[sortHeaderId]); // 优先级:纯数字 < 数字开头的文本 < 纯文本(可以根据需求调整顺序) if (typeof valueA === 'number' && typeof valueB === 'string') { return -1; // 数字排在前面 } if (typeof valueA === 'string' && typeof valueB === 'number') { return 1; // 文本排在数字后面 } // 同类型内容直接比较 if (valueA < valueB) { return -1; } if (valueA > valueB) { return 1; } return 0; // 相等 } // 提取用于排序的值:能转成有效数字就转,否则保留原字符串 private extractSortValue(value: string): string | number { const num = Number(value); // 检查是否是有效数字(排除NaN、Infinity等情况) if (!isNaN(num) && isFinite(num)) { return num; } // 如果你希望数字开头的文本(比如'1tesxt')和对应纯数字('1')排在一起,可以修改这里的逻辑 // 比如提取开头的数字部分,示例代码我放在后面了 return value; }
2. 替换数据源的默认排序方法
在组件初始化的时候,把我们的自定义排序逻辑绑定到MatTableDataSource的sortData方法上:
dataSource = new MatTableDataSource<YourDataModel>([]); // 初始化你的数据源 ngOnInit() { // 替换默认排序逻辑 this.dataSource.sortData = (data: YourDataModel[], sort: MatSort) => { if (!sort.active || sort.direction === '') { return data; // 没有排序指令时返回原数据 } return data.sort((a, b) => { const sortResult = this.customSortData(a, b, sort.active); // 根据升序/降序调整结果方向 return sort.direction === 'asc' ? sortResult : -sortResult; }); }; }
3. 确保模板的排序指令正确绑定
你的模板代码已经有matSort和mat-sort-header了,只需要确保结构正确就行:
<mat-table #table [dataSource]="dataSource" matSort > <ng-container matColumnDef="model"> <mat-header-cell *matHeaderCellDef mat-sort-header> Model </mat-header-cell> <mat-cell *matCellDef="let element">{{element.model}}</mat-cell> </ng-container> <!-- 其他列的定义 --> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> </mat-table>
可选:让数字开头的文本和纯数字排在一起
如果你希望'1'、'1tesxt'、'12'、'12abc'这样的内容按开头数字排序,再按后面的文本排序,可以修改extractSortValue和customSortData的逻辑:
// 修改后的提取值函数 private extractSortValue(value: string): string | number | [number, string] { // 提取字符串开头的连续数字 const numMatch = value.match(/^\d+/); if (numMatch) { const num = Number(numMatch[0]); if (!isNaN(num) && isFinite(num)) { // 返回[开头数字, 剩余文本]的数组,方便先比数字再比文本 return [num, value.slice(numMatch[0].length)]; } } // 纯文本或无法提取数字的情况 return value; } // 修改后的排序对比逻辑 customSortData(a: YourDataModel, b: YourDataModel, sortHeaderId: string): number { const valueA = this.extractSortValue(a[sortHeaderId]); const valueB = this.extractSortValue(b[sortHeaderId]); // 处理数字+文本的数组类型 if (Array.isArray(valueA) && Array.isArray(valueB)) { // 先比数字部分 if (valueA[0] !== valueB[0]) { return valueA[0] - valueB[0]; } // 数字相同则比剩余文本 return valueA[1].localeCompare(valueB[1]); } // 处理数字和数组的情况 if (typeof valueA === 'number' && Array.isArray(valueB)) { return valueA - valueB[0]; } if (Array.isArray(valueA) && typeof valueB === 'number') { return valueA[0] - valueB; } // 数字和纯文本的优先级 if (typeof valueA === 'number' && typeof valueB === 'string') { return -1; } if (typeof valueA === 'string' && typeof valueB === 'number') { return 1; } // 纯文本之间用localeCompare做自然排序 if (typeof valueA === 'string' && typeof valueB === 'string') { return valueA.localeCompare(valueB); } return 0; }
这样调整后,排序结果就会变成:1, 1, 1tesxt, 12, OPD, OXD, XXX(升序的情况),完全符合我们的预期啦!
内容的提问来源于stack exchange,提问作者Roja Fernando
相关产品推荐
相关产品推荐

