Angular中使用async管道的Observable排序后表格不更新如何解决?
问题原因
- 你当前的
dataSource$仅由过滤流和静态数据源组合生成,排序操作没有触发该流发射新值,async管道无法感知到排序变化,因此视图不会更新 - 指令中对数组执行的
sort是原地排序操作,即使修改了数组内容,也不会触发流的重新发射 - 现有排序逻辑写死了排序字段,无法适配不同表头列的排序需求
修复步骤
1. 组件新增排序状态流
在组件中新增BehaviorSubject存储当前排序配置,将其加入combineLatest的依赖中,这样排序参数变化时会自动触发数据源重新计算:
// 组件ts中新增代码 import { BehaviorSubject, combineLatest, of } from "rxjs"; import { map } from "rxjs/operators"; // ... 原有代码保持不变 // 新增排序状态存储,初始可以按firstname正序排列 sortSubject$ = new BehaviorSubject<{sortKey: string, sortOrder: 'asc'|'desc'}>({ sortKey: 'firstname', sortOrder: 'asc' }); sort$ = this.sortSubject$.asObservable(); // 修改原有dataSource$的定义,加入排序逻辑 dataSource$ = combineLatest([ this.filter$, of(this.dataSource), this.sort$ ]).pipe( map(([filter, dataSource, sortConfig]) => { // 先执行过滤逻辑 const filteredData = dataSource.filter(item => filter.every(({key, value}) => new RegExp(String(value).toLowerCase()).test(String(item[key]).toLowerCase()) ) ); // 拷贝数组后排序,避免修改原始数据源 return [...filteredData].sort((a, b) => { const valA = a[sortConfig.sortKey]; const valB = b[sortConfig.sortKey]; // 数字类型直接比较,字符串转小写后比较 if (typeof valA === 'number' && typeof valB === 'number') { return sortConfig.sortOrder === 'asc' ? valA - valB : valB - valA; } const strA = String(valA).toLowerCase(); const strB = String(valB).toLowerCase(); if (strA < strB) return sortConfig.sortOrder === 'asc' ? -1 : 1; if (strA > strB) return sortConfig.sortOrder === 'asc' ? 1 : -1; return 0; }); }) )
2. 修改自定义排序指令
指令不再直接操作数据源流,改为接收排序状态的BehaviorSubject,点击表头时更新排序参数即可:
import { Directive, Input, ElementRef, HostListener } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Directive({ selector: '[appSortObservable]' }) export class SortObservableDirective { // 传入排序状态subject @Input() appSortObservable: BehaviorSubject<{sortKey: string, sortOrder: 'asc'|'desc'}>; constructor(private targetElem: ElementRef) { } @HostListener("click") sortData() { // 读取当前表头绑定的排序字段 const sortKey = this.targetElem.nativeElement.getAttribute('data-name'); // 切换排序顺序 const currentOrder = this.targetElem.nativeElement.getAttribute('data-order') as 'asc'|'desc'; const newOrder = currentOrder === 'asc' ? 'desc' : 'asc'; // 更新dom属性存储最新排序顺序 this.targetElem.nativeElement.setAttribute('data-order', newOrder); // 发射新的排序配置,触发数据源重新计算 this.appSortObservable.next({sortKey, sortOrder: newOrder}); } }
3. 修改HTML表头绑定
将原有传入dataSource$改为传入组件的sortSubject$:
<thead> <tr> <th scope="col" [appSortObservable]="sortSubject$" data-order="asc" data-name="firstname">first name</th> <th scope="col" [appSortObservable]="sortSubject$" data-order="asc" data-name="lastname">last name</th> <th scope="col" [appSortObservable]="sortSubject$" data-order="asc" data-name="othernames">other names</th> <th scope="col" [appSortObservable]="sortSubject$" data-order="asc" data-name="age">age</th> </tr> </thead>
修改完成后点击表头即可触发排序,同时和原有的过滤逻辑完全兼容。
内容的提问来源于stack exchange,提问作者Petar
相关产品推荐
相关产品推荐

