Angular Material - 同时按多个过滤器和多个过滤器值过滤 Table
Angular Material - Filter Table by multiple filters and multiple filter values at the same time
我正在尝试能够为 table 个对象设置两个不同的过滤器。第一个是基于 text/input 的普通过滤器,按预期工作。
我试图开始工作的第二个是一行标记为“1 级”、“2 级”等的复选框。选中一个复选框后,我想按“级别”列过滤所有当前选中的复选框。理想情况下,用户可以通过文本和级别选择
过滤 table
我读过有关使用 filterPredicate 的信息并尝试使用 this as a template 但我一定遗漏了一些东西。
当前代码片段:
HTML:
//Input for text filter
<mat-form-field >
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter...">
</mat-form-field>
...
//Checkboxes for level filter
<div fxLayout="row" fxLayoutAlign="space-between center" class="margin-1">
<mat-checkbox (change)="customFilterPredicate()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.level}}</mat-checkbox>
</div>
TS
ngOnInit() {
this.dataSource.filterPredicate = this.customFilterPredicate();
}
...
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
customFilterPredicate() {
const myFilterPredicate = (data): boolean => {
return this.levelsToShow.some(data['level'].toString().trim());
};
return myFilterPredicate;
}
更新
到目前为止,感谢 Fabian Küng,我取得了一些进步,但我仍然没有回家。澄清一下,我希望文本过滤器能够搜索多列('castingTime'、'duration' 等),并让复选框仅按级别过滤。
现在当我点击一个复选框时我得到这个错误:
'无法读取未定义的 属性 'toLowerCase''
指向这行代码:
return data['level'].toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
但是当我控制台注销数据时,我可以看到它有一个级别 属性,所以我看不出哪里出错了。
以下是相关的代码片段:
HTML:
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter..." [formControl]="textFilter">
<mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>
TS:
levelsToShow: Level[] = [
{ level: '1', active: false, name: 'Level 1' },
{ level: '2', active: false, name: 'Level 2' },
{ level: '3', active: false, name: 'Level 3' },
{ level: '4', active: false, name: 'Level 4' },
{ level: '5', active: false, name: 'Level 5' },
{ level: '6', active: false, name: 'Level 6' },
{ level: '7', active: false, name: 'Level 7' },
{ level: '8', active: false, name: 'Level 8' },
{ level: '9', active: false, name: 'Level 9' }
];
levelFilter = new FormControl();
textFilter = new FormControl();
globalFilter = '';
filteredValues = {
level: '',
text: '',
};
ngOnInit() {
...
this.textFilter.valueChanges.subscribe((textFilterValue) => {
this.filteredValues['text'] = textFilterValue;
this.dataSource.filter = JSON.stringify(this.filteredValues);
});
this.dataSource.filterPredicate = this.customFilterPredicate();
}
customFilterPredicate() {
const myFilterPredicate = (data: Spell, filter: string): boolean => {
var globalMatch = !this.globalFilter;
if (this.globalFilter) {
// search all text fields
globalMatch = data['spellName'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['level'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['castingTime'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['distance'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['details'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['duration'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['school'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['effect'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1;
}
if (!globalMatch) {
return;
}
let searchString = JSON.parse(filter);
return data['level'].toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
(this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
this.levelsToShow.filter(level => level.active).some(level => level.level === data.level.toString()));
}
return myFilterPredicate;
}
updateFilter() {
this.dataSource.filter = JSON.stringify(this.filteredValues);
}
更新二
此处要求的是 table 行之一的示例:(这是 d&d table 法术)
{
castingTime: "1 half action"
distance: "Short range attack"
duration: "1 round per Casting Level"
effect: "Once per round, you may take a half action to launch an arrow of acid from your palm, generating a new Spellcasting result to see if you hit.Each arrow inflicts 1d6 acid damage.↵ "
index: 0
level: "4"
school: "Creation"
spellName: "ACID ARROW"
}
最终更新
这就是我最后的结果,以防有人卡住。感谢 Fabian Küng 解决了这一切!唯一奇怪的是,我最终使用了文本输入“textFilter”的实际值,因为将文本 filteredValues 字符串化然后解析它们(据我所知,过滤器只接受字符串)一直给我“JSON 错误,意外值为 0" 消息,据我所知,这可以正常工作,除了现在我需要弄清楚如何限制过滤器。
customFilterPredicate() {
const myFilterPredicate = (data: Spell, filter: string): boolean => {
var globalMatch = !this.globalFilter;
if (this.globalFilter) {
// search all text fields
globalMatch = data['spellName'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['level'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['castingTime'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['distance'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['details'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['duration'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['school'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['effect'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1;
}
if (!globalMatch) {
return;
}
return data.spellName.toString().trim().toLowerCase().indexOf(this.textFilter.value) !== -1 &&
(this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
this.levelsToShow.filter(level => level.active).some(level => level.level === data.level.toString()));
}
return myFilterPredicate;
}
您在设置复选框然后使用复选框模型在 customFilterPredicate
函数中进行过滤方面走在了正确的轨道上。
I took the linked stackblitz and modified it to make it work with your
checkboxes, check it
here.
在模板中,您有自己设置的复选框:
<mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>
在组件中我添加了一个Level
接口:
export interface Level {
active: boolean;
name: string;
level: number;
}
和一些数据:
levelsToShow: Level[] = [
{ level: 1, active: false, name: 'Level 1' },
{ level: 2, active: false, name: 'Level 2' },
{ level: 3, active: false, name: 'Level 3' },
];
而最重要的部分在 customFilterPredicate
函数中。它所做的是,它在第一个条件中过滤 name
属性,在第二个条件中它检查是否所有级别都设置为 false
,在这种情况下我们 return true
因为我假设您希望在未选中复选框的情况下查看所有级别。如果某个级别处于活动状态,我们将按该级别进行过滤,如果选择了多个级别,则按多个级别进行过滤。
return data.name.toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
(this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
this.levelsToShow.filter(level => level.active).some(level => level.level === data.level));
一个小但重要的部分是当您 check/uncheck 一个复选框时实际触发过滤。这可以通过再次设置数据源 filter
来完成,并在复选框值更改时调用:
updateFilter() {
this.dataSource.filter = JSON.stringify(this.filteredValues);
}
如果您使用 @angular/material
,您可以使用 mat-table-filter 来满足复杂的过滤要求。
勾选这个
更简单的方法
在数据源中使用 FilterPredicate 设置多个表达式
使用表单组分配将发送 filterValue 的控件
<form [formGroup]="filterForm">
<mat-form-field>
<input matInput placeholder="Model" formControlName="model" />
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Type" formControlName="modelType" />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Status</mat-label>
<mat-select formControlName="status">
<mat-option value=""></mat-option>
<mat-option value="Active">Active</mat-option>
<mat-option value="InActive">InActive</mat-option>
</mat-select>
</mat-form-field>
#TypeScript
指定 filterPredicate 表达式
// Filter Predicate Expression to perform Multiple KeyWord Search
this.dataSource.filterPredicate = ((data: DeviceInventory, filter) => {
const a = !filter.model || data.modelCode.trim().toLowerCase().includes(filter.model);
const b = !filter.modelType || data.modelType.trim().toLowerCase().includes(filter.modelType);
return a && b;
}) as (DeviceInventory, string) => boolean;
订阅FomrGroup值变化监听filterValues
this.filterForm.valueChanges.subscribe(value => {
this.dataSource.filter = value;
console.log(value);
});
}
我正在尝试能够为 table 个对象设置两个不同的过滤器。第一个是基于 text/input 的普通过滤器,按预期工作。
我试图开始工作的第二个是一行标记为“1 级”、“2 级”等的复选框。选中一个复选框后,我想按“级别”列过滤所有当前选中的复选框。理想情况下,用户可以通过文本和级别选择
过滤 table我读过有关使用 filterPredicate 的信息并尝试使用 this as a template 但我一定遗漏了一些东西。
当前代码片段:
HTML:
//Input for text filter
<mat-form-field >
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter...">
</mat-form-field>
...
//Checkboxes for level filter
<div fxLayout="row" fxLayoutAlign="space-between center" class="margin-1">
<mat-checkbox (change)="customFilterPredicate()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.level}}</mat-checkbox>
</div>
TS
ngOnInit() {
this.dataSource.filterPredicate = this.customFilterPredicate();
}
...
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
customFilterPredicate() {
const myFilterPredicate = (data): boolean => {
return this.levelsToShow.some(data['level'].toString().trim());
};
return myFilterPredicate;
}
更新
到目前为止,感谢 Fabian Küng,我取得了一些进步,但我仍然没有回家。澄清一下,我希望文本过滤器能够搜索多列('castingTime'、'duration' 等),并让复选框仅按级别过滤。
现在当我点击一个复选框时我得到这个错误: '无法读取未定义的 属性 'toLowerCase'' 指向这行代码:
return data['level'].toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
但是当我控制台注销数据时,我可以看到它有一个级别 属性,所以我看不出哪里出错了。
以下是相关的代码片段:
HTML:
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter..." [formControl]="textFilter">
<mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>
TS:
levelsToShow: Level[] = [
{ level: '1', active: false, name: 'Level 1' },
{ level: '2', active: false, name: 'Level 2' },
{ level: '3', active: false, name: 'Level 3' },
{ level: '4', active: false, name: 'Level 4' },
{ level: '5', active: false, name: 'Level 5' },
{ level: '6', active: false, name: 'Level 6' },
{ level: '7', active: false, name: 'Level 7' },
{ level: '8', active: false, name: 'Level 8' },
{ level: '9', active: false, name: 'Level 9' }
];
levelFilter = new FormControl();
textFilter = new FormControl();
globalFilter = '';
filteredValues = {
level: '',
text: '',
};
ngOnInit() {
...
this.textFilter.valueChanges.subscribe((textFilterValue) => {
this.filteredValues['text'] = textFilterValue;
this.dataSource.filter = JSON.stringify(this.filteredValues);
});
this.dataSource.filterPredicate = this.customFilterPredicate();
}
customFilterPredicate() {
const myFilterPredicate = (data: Spell, filter: string): boolean => {
var globalMatch = !this.globalFilter;
if (this.globalFilter) {
// search all text fields
globalMatch = data['spellName'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['level'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['castingTime'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['distance'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['details'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['duration'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['school'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['effect'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1;
}
if (!globalMatch) {
return;
}
let searchString = JSON.parse(filter);
return data['level'].toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
(this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
this.levelsToShow.filter(level => level.active).some(level => level.level === data.level.toString()));
}
return myFilterPredicate;
}
updateFilter() {
this.dataSource.filter = JSON.stringify(this.filteredValues);
}
更新二
此处要求的是 table 行之一的示例:(这是 d&d table 法术)
{
castingTime: "1 half action"
distance: "Short range attack"
duration: "1 round per Casting Level"
effect: "Once per round, you may take a half action to launch an arrow of acid from your palm, generating a new Spellcasting result to see if you hit.Each arrow inflicts 1d6 acid damage.↵ "
index: 0
level: "4"
school: "Creation"
spellName: "ACID ARROW"
}
最终更新
这就是我最后的结果,以防有人卡住。感谢 Fabian Küng 解决了这一切!唯一奇怪的是,我最终使用了文本输入“textFilter”的实际值,因为将文本 filteredValues 字符串化然后解析它们(据我所知,过滤器只接受字符串)一直给我“JSON 错误,意外值为 0" 消息,据我所知,这可以正常工作,除了现在我需要弄清楚如何限制过滤器。
customFilterPredicate() {
const myFilterPredicate = (data: Spell, filter: string): boolean => {
var globalMatch = !this.globalFilter;
if (this.globalFilter) {
// search all text fields
globalMatch = data['spellName'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['level'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['castingTime'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['distance'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['details'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['duration'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['school'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data['effect'].toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1;
}
if (!globalMatch) {
return;
}
return data.spellName.toString().trim().toLowerCase().indexOf(this.textFilter.value) !== -1 &&
(this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
this.levelsToShow.filter(level => level.active).some(level => level.level === data.level.toString()));
}
return myFilterPredicate;
}
您在设置复选框然后使用复选框模型在 customFilterPredicate
函数中进行过滤方面走在了正确的轨道上。
I took the linked stackblitz and modified it to make it work with your checkboxes, check it here.
在模板中,您有自己设置的复选框:
<mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>
在组件中我添加了一个Level
接口:
export interface Level {
active: boolean;
name: string;
level: number;
}
和一些数据:
levelsToShow: Level[] = [
{ level: 1, active: false, name: 'Level 1' },
{ level: 2, active: false, name: 'Level 2' },
{ level: 3, active: false, name: 'Level 3' },
];
而最重要的部分在 customFilterPredicate
函数中。它所做的是,它在第一个条件中过滤 name
属性,在第二个条件中它检查是否所有级别都设置为 false
,在这种情况下我们 return true
因为我假设您希望在未选中复选框的情况下查看所有级别。如果某个级别处于活动状态,我们将按该级别进行过滤,如果选择了多个级别,则按多个级别进行过滤。
return data.name.toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&
(this.levelsToShow.filter(level => !level.active).length === this.levelsToShow.length ||
this.levelsToShow.filter(level => level.active).some(level => level.level === data.level));
一个小但重要的部分是当您 check/uncheck 一个复选框时实际触发过滤。这可以通过再次设置数据源 filter
来完成,并在复选框值更改时调用:
updateFilter() {
this.dataSource.filter = JSON.stringify(this.filteredValues);
}
如果您使用 @angular/material
,您可以使用 mat-table-filter 来满足复杂的过滤要求。
勾选这个
更简单的方法
在数据源中使用 FilterPredicate 设置多个表达式
使用表单组分配将发送 filterValue 的控件
<form [formGroup]="filterForm">
<mat-form-field>
<input matInput placeholder="Model" formControlName="model" />
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Type" formControlName="modelType" />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Status</mat-label>
<mat-select formControlName="status">
<mat-option value=""></mat-option>
<mat-option value="Active">Active</mat-option>
<mat-option value="InActive">InActive</mat-option>
</mat-select>
</mat-form-field>
#TypeScript 指定 filterPredicate 表达式
// Filter Predicate Expression to perform Multiple KeyWord Search
this.dataSource.filterPredicate = ((data: DeviceInventory, filter) => {
const a = !filter.model || data.modelCode.trim().toLowerCase().includes(filter.model);
const b = !filter.modelType || data.modelType.trim().toLowerCase().includes(filter.modelType);
return a && b;
}) as (DeviceInventory, string) => boolean;
订阅FomrGroup值变化监听filterValues
this.filterForm.valueChanges.subscribe(value => {
this.dataSource.filter = value;
console.log(value);
});
}