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()
函数中的任何内容。
如其他答案中所述,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.
上的一个工作示例
我试图使用 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()
函数中的任何内容。
如其他答案中所述,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.
上的一个工作示例