【问题标题】:Angular Material mat-table not returning any results upon filterAngular Material mat-table 在过滤时不返回任何结果
【发布时间】:2020-01-02 03:13:26
【问题描述】:

我尝试使用 Angular Material 包重新创建垫表。 mat-table 示例最近得到了大规模改进,这令人惊叹。他们提供了这个example here with both sort and filter features。

我试图在我自己的应用程序的上下文中重现这一点,在 stackblitz here 的一个最小示例中使用虚拟数据。

表格显示并填充了数据,但一旦我输入多个字母,过滤方法似乎会错误地删除所有条目,即使是不应该删除的条目。

我能想到的一个区别是,在我的示例中,我在数据源的声明中使用了一个类:dataSource: MatTableDataSource<Match>;,而在 Angular 人员提供的示例中,它是一个接口:@987654324 @。

不过,这很可能是一个红鲱鱼,因为我尝试使用界面而不是成功。非常感谢这里的任何帮助。

【问题讨论】:

    标签: angular angular-material angular-material-table


    【解决方案1】:

    在官方的 Angular stackblitz 示例中,dataSource 有一个 1 级深度的对象数组,通过 console.log(this.dataSource.filteredData) 可以明显看出:

    但是,您的 dataSource 在 Match 对象中嵌套了另一层对象两次(即 MatchDetails 和 MoveInVideo 对象嵌套在 Match 对象中):

    因此,提供的过滤器功能对您不起作用,因为它仅根据过滤器值检查第一级对象(匹配)。事实上,它通过返回[] 可以正常工作,因为您的 Match 对象中没有任何值与过滤器值匹配。如果您搜索“originalFilterPosterId”,它将返回所有对象,因为所有匹配对象都包含此值。

    您可以通过在调用applyFilter() 事件处理程序之前在 ngOnInit() 中设置通用filterPredicate 来解决此问题。这个通用函数将能够过滤嵌套对象。所有感谢 @Sagar Kharche 和 @mighty_mite Stackover answer source。

    ngOnInit(){
      this.dataSource.paginator = this.paginator;
      this.dataSource.sort = this.sort;
    
      //set a new filterPredicate function
      this.dataSource.filterPredicate = (data, filter: string) => {
        const accumulator = (currentTerm, key) => {
          return this.nestedFilterCheck(currentTerm, data, key);
        };
        const dataStr = Object.keys(data).reduce(accumulator, '').toLowerCase();
        // Transform the filter by converting it to lowercase and removing whitespace.
        const transformedFilter = filter.trim().toLowerCase();
        return dataStr.indexOf(transformedFilter) !== -1;
      }
    }
    
    //also add this nestedFilterCheck class function
    nestedFilterCheck(search, data, key) {
      if (typeof data[key] === 'object') {
        for (const k in data[key]) {
          if (data[key][k] !== null) {
            search = this.nestedFilterCheck(search, data[key], k);
          }
        }
      } else {
        search += data[key];
      }
      return search;
    }
    
    

    您无需更改 applyFilter() 函数中的任何内容。

    Forked Stackblitz Example ⚡

    【讨论】:

    • 非常感谢!顺便说一句,你知道为什么 sort 和 paginator 都不起作用吗?
    • 排序不工作,原因与过滤器不工作的原因相同,嵌套对象。您可以通过覆盖 dataSource.sortingDataAccessor 轻松解决此问题,请参阅stackoverflow question。我不确定您的分页器是如何工作的..?
    • 由于您只展示了应用程序的一小部分,我不确定您是否真的需要将一组嵌套对象存储在您的应用程序中。没有必要模仿 API 响应。 @nash 的解决方案也有效,但我要补充他的解决方案,说您可以创建另一个模型类,即一个 1 级深度 的“ViewModel 类”,专门用于此表。因此,将来如果您想从其他对象(例如 MoveInVideo)添加更多要显示的列,您可以简单地向此 ViewModel 类添加另一个属性,映射您的 api 响应值并显示阵列出来。
    • 谢谢。在关键时刻救了我。
    【解决方案2】:

    通常,过滤器的工作原理是将对象的第一级数据序列化为字符串,然后快速检查字符串是否包含。这不适用于深层物体。所以不能依赖默认过滤,需要提供filterPredicate。

    在documents 它说

    要覆盖默认过滤行为,可以设置自定义filterPredicate 函数,该函数接受数据对象和过滤字符串,如果数据对象被视为匹配则返回true。

    仅按位置过滤的示例:

        this.dataSource.filterPredicate = (match: Match, filter: string) => {
          return match.matchDeets.location.toLowerCase().includes(filter);
        };
    

    【讨论】:

      【解决方案3】:

      如其他答案中所述,filter 函数仅过滤一层深度。不过,在您的问题中,我认为您的数据一开始就没有任何原因。在您的模板中,您只显示来自matchDeets 的项目,所以为什么不直接使用matchDeets 作为您的dataSource。这将使您的过滤器功能再次起作用。

      在您的组件中,只需将dataSource 更改为以下内容。

      dataSource: MatTableDataSource<MatchDetails>;
      
      // Other code goes here
      
      this.dataSource = new MatTableDataSource(matchArray.map(match => match.matchDeets));
      

      此更改将是不可变的,因此matchArray 仍将包含您班级的其他数据。由于您的模板似乎并不关心它们,我们只需将我们需要的传递给dataSouce,即matchDeets。

      现在在您的模板中,您只需将显示值从{{entry.matchDeets.rank}} 更新为{{entry.rank}}、{{entry.matchDeets.weightClass}} 到{{entry.weightClass}} 等等。

      您的过滤器功能现在可以工作了。这是StackBlitz 上的一个工作示例。

      【讨论】:

        【解决方案4】:

        为了允许任何类型的数组,MatTableDataSource 在模型中应该如下所示。

          dataSource = new MatTableDataSource<any>();
        

        清除过滤器:

        this.dataSource.filter="";
        

        查看要显示的内容:

        this.dataSource.data
        

        从输入元素挂钩 keydown 和 keyup 开始过滤过程

        <input id='search' #search  matinput type='text' (keydown)="onKeyDown(search,$event)" (keyup)='onSearchChanged(search, $event)' />
        
        //this catches the backspace
        onKeyDown(searchInput, event) {
              if (event.key === "Backspace") {
                 this.filter = this.dataSource.filter = searchInput.value;
                 this.cdf.detectChanges();
              }
           }
        
        //this changes the filter as they type in the input field
        onSearchChanged(search, event) {
              this.filter = search.value;
              this.dataSource.filter = this.filter;
              this.cdf.detectChanges();
           }
        

        如果没有显示任何内容,您必须确保已设置列标题和列 ID。

        【讨论】:

        • 谢谢你,约翰!我正在使用的示例,并且我已经拥有许多这些功能。能否麻烦您看一下我提供的 stackblitz 示例?
        猜你喜欢
        • 2019-10-10
        • 1970-01-01
        • 2020-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多