清除搜索筛选后嵌套Mat-Table无法重置的技术问题
嵌套Mat-Table搜索筛选后无法重置的问题解决
问题现象
实现了带Mat展开面板的嵌套Mat-Table,支持搜索筛选:用户输入内容匹配子表数据时,父行会自动展开并显示匹配结果。但清除搜索输入后,表格无法恢复初始状态——比如打开第三行原本有2条子表数据,搜索“Ohio”后显示1条匹配数据,清除搜索后再打开第三行,还是只能看到1条数据,不符合“完全重置”的预期。
问题根源
核心问题是直接修改了原始数据对象的addresses属性:
toggleRow方法中,把原本的数组类型addresses替换成了MatTableDataSource,覆盖了原数据的该属性;applyFilter的filterPredicate里,同样直接修改data.addresses为筛选后的数据源,导致原始数据被永久污染;- 重置时虽然把
dataSource.data设为this.usersData,但此时this.usersData里的对象已经被修改,不再是初始的完整地址数组。
解决方案
维护一份原始数据的深拷贝,所有筛选操作基于原始数据副本进行,绝不修改原始数据对象。
步骤1:保存原始数据副本
在组件中新增变量存储原始数据的深拷贝,初始化时完成复制:
private originalUsersData: User[]; ngOnInit() { // 深拷贝原始数据,避免后续操作污染初始数据 this.originalUsersData = JSON.parse(JSON.stringify(this.usersData)); this.dataSource = new MatTableDataSource(this.usersData); // 其他初始化逻辑保持不变 }
步骤2:修改toggleRow方法
不再修改原始数据的addresses,而是给元素添加临时属性存储筛选后的数据源:
toggleRow(element: User) { // 从原始数据中获取当前用户的完整地址数组 const originalUser = this.originalUsersData.find(u => u.id === element.id); if (!originalUser?.addresses || !Array.isArray(originalUser.addresses)) { this.expandedElement = this.expandedElement === element ? null : element; return; } // 基于原始地址数组做筛选,不碰原数据 const filteredAddresses = originalUser.addresses.filter(address => { return address.street.toLowerCase().includes(this.searchFilter.toLowerCase()) || address.zipCode.toLowerCase().includes(this.searchFilter.toLowerCase()) || address.city.toLowerCase().includes(this.searchFilter.toLowerCase()); }); // 用临时属性存储筛选结果,避免污染原始addresses element.filteredAddresses = new MatTableDataSource(filteredAddresses); this.expandedElement = this.expandedElement === element ? null : element; this.cd.detectChanges(); this.innerTables.forEach((table, index) => { (table.dataSource as MatTableDataSource<Address>).sort = this.innerSort.toArray()[index]; }); }
步骤3:修改applyFilter方法
重置时使用原始数据的深拷贝恢复,筛选时基于原始数据判断,同样用临时属性存储子表数据源:
applyFilter(filterValue: string) { filterValue = filterValue.trim().toLowerCase(); if (!filterValue) { // 用原始数据的深拷贝重置表格,确保完全恢复初始状态 this.dataSource.data = JSON.parse(JSON.stringify(this.originalUsersData)); this.dataSource.filter = ''; this.expandedElement = null; this.searchFilter = ''; } else { this.searchFilter = filterValue; this.dataSource.filterPredicate = (data: User, filter: string) => { // 先检查父表字段是否匹配 const parentMatch = data.name.toLowerCase().includes(filter) || data.email.toLowerCase().includes(filter) || data.phone.toLowerCase().includes(filter); if (parentMatch) return true; // 从原始数据中取完整地址数组做匹配判断 const originalUser = this.originalUsersData.find(u => u.id === data.id); if (!originalUser?.addresses) return false; const hasMatchingAddress = originalUser.addresses.some(a => a.street.toLowerCase().includes(filter) || a.zipCode.toLowerCase().includes(filter) || a.city.toLowerCase().includes(filter) ); // 匹配成功则给当前元素添加临时筛选数据源 if (hasMatchingAddress) { const filteredAddresses = originalUser.addresses.filter(a => a.street.toLowerCase().includes(filter) || a.zipCode.toLowerCase().includes(filter) || a.city.toLowerCase().includes(filter) ); data.filteredAddresses = new MatTableDataSource(filteredAddresses); } return hasMatchingAddress; }; this.dataSource.filter = filterValue; this.expandedElement = this.dataSource.filteredData[0] || null; } }
步骤4:修改模板绑定
把子表的数据源从element.addresses改成临时属性element.filteredAddresses:
<!-- 嵌套子表部分 --> <table mat-table [dataSource]="element.filteredAddresses" matSort> <!-- 子表列定义保持不变 --> </table>
内容的提问来源于stack exchange,提问作者Teja
相关产品推荐
相关产品推荐

