Angular Mat Table 2选择模型传入对象副本时为何失效?
这个坑我之前做Angular Mat Table批量选择时也踩过!Angular Material的SelectionModel是靠对象引用来判断选中状态的,不是对象的内容。你从数据集里筛选出来的对象,哪怕和表格行里的对象属性全一样,但只要是两个不同的对象实例(引用不同),==或===比较都会返回false——结果就是你把筛选的对象加进了选择数组,但SelectionModel认不出它和表格行的对象是同一个,视觉上就不会显示选中,后续操作也没法同步状态。
针对这个问题,有几个递进的解决思路,推荐优先用第一个:
1. 直接用表格数据源的原始对象引用
实现Shift批量选择时,别从外部数据集重新筛选对象,直接从表格绑定的DataSource里拿对应的行对象——这样能保证引用完全一致。比如你用的是MatTableDataSource,可以这样写:
// 先记录上一次选中行的索引(比如在Ctrl点击时保存) private lastSelectedIndex: number; // Shift点击触发的批量选择逻辑 onShiftSelect(currentIndex: number) { if (this.lastSelectedIndex === undefined) return; const startIdx = Math.min(this.lastSelectedIndex, currentIndex); const endIdx = Math.max(this.lastSelectedIndex, currentIndex); // 直接从dataSource的data数组中取对象,保证引用和表格行一致 const batchItems = this.dataSource.data.slice(startIdx, endIdx + 1); batchItems.forEach(item => this.selection.select(item)); // 更新最后选中的索引 this.lastSelectedIndex = currentIndex; }
2. 给SelectionModel自定义比较规则
如果因为业务原因,实在没法保证引用一致(比如数据是从不同接口拉取的),可以在初始化SelectionModel时,传入一个自定义的比较函数,用数据项的唯一标识(比如id)来判断是否为同一项,而不是默认的引用比较:
// 假设你的数据类型是YourData,有唯一id字段 this.selection = new SelectionModel<YourData>( true, // 允许多选 [], // 默认选中项 true, // 选中变化时触发事件 (itemA, itemB) => itemA.id === itemB.id // 自定义比较器,用id判断 );
这样哪怕对象引用不同,只要id一样,SelectionModel就会认为是同一个项,视觉状态就能同步了。
3. 强制触发视图更新(应急方案)
如果上面两种方法暂时没法改,可以试试手动触发Angular的变更检测,让表格重新渲染选中状态:
// 先注入ChangeDetectorRef import { ChangeDetectorRef } from '@angular/core'; constructor(private cdr: ChangeDetectorRef) {} // 批量选择完成后调用 onShiftSelect(currentIndex: number) { // ...你的批量选择逻辑... // 强制触发变更检测 this.cdr.detectChanges(); }
不过这个只是临时救急,还是推荐从根源上解决引用不一致的问题,避免后续出现其他奇怪的bug。
你可以在控制台打印一下,确认添加到SelectionModel的对象和表格行的对象是不是同一个引用:
// 比如在批量选择时打印 const item = this.dataSource.data[currentIndex]; console.log(item === this.selection.selected.find(s => s.id === item.id));
如果输出是false,那肯定是引用不一致,得调整获取对象的方式;如果是true,那再检查其他逻辑。
内容的提问来源于stack exchange,提问作者tatsu

