Angular Material mat-table 过滤后未返回任何结果

Angular Material mat-table not returning any results upon filter

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

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

table 显示并填充了数据,但只要我键入一个以上的字母,过滤方法似乎就会错误地删除所有条目,甚至是不应该删除的条目。

我在这里能想到的一个区别是,在我的示例中,我在数据源声明中使用了 class:dataSource: MatTableDataSource<Match>;,而在 dataSource: MatTableDataSource<Match>; 提供的示例中=27=] 各位,这是一个界面:dataSource: MatTableDataSource<UserData>;.

不过,这很可能是转移注意力,因为我尝试使用界面而不是成功。非常感谢这里的任何帮助。

要允许任何类型的数组,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 都已设置。

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

documents 它说

To override the default filtering behavior, a custom filterPredicate function can be set which takes a data object and filter string and returns true if the data object is considered a match.

仅按位置过滤的示例:

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

在官方 Angular stackblitz 示例中,dataSource 有一个 对象数组,深度为 1 级 ,通过执行 console.log(this.dataSource.filteredData):

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

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

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

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 ⚡

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

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

dataSource: MatTableDataSource<MatchDetails>;

// Other code goes here

this.dataSource = new MatTableDataSource(matchArray.map(match => match.matchDeets));

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

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

您的过滤功能现在可以使用了。这是 StackBlitz.

上的一个工作示例