You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

清除搜索筛选后嵌套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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 21:59:52