Angular Material - 同时通过多个过滤器和多个过滤器值过滤表

标签 angular typescript angular-material material-design angular-material2

我正在尝试能够为一个对象表设置两个不同的过滤器。第一个是普通的基于文本/输入的过滤器,按预期工作。

我要尝试的第二个是一行标记为“1 级”、“2 级”等的复选框。选中一个复选框时,我想按“级别”列过滤所有当前选中的复选框。理想情况下,用户可以通过文本和级别选择来过滤表格

我读过有关使用 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 &&

但是当我控制台注销数据时,我可以看到它有一个 level 属性,所以我没有看到哪里出错了。

以下是相关的代码片段:

    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);
  }

更新二

此处要求的是其中一个表格行的示例:(这是一个 d&d 法术表格)

{
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 错误, unexpected value at 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接口(interface):

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,在这种情况下我们返回 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));

一个小但重要的部分是当您选中/取消选中复选框时实际触发过滤。这可以通过再次设置数据源 filter 来完成,并在复选框值更改时调用:

updateFilter() {
  this.dataSource.filter = JSON.stringify(this.filteredValues);
}

关于Angular Material - 同时通过多个过滤器和多个过滤器值过滤表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54375826/

相关文章:

Angular Material 重置按钮切换组

Angular 对话框不显示默认值

node.js - typescript 错误: TS2339:Property 'map' does not exist on type 'string'

javascript - Angular Material MdSidebar - 如何从 typescript 调用 .toggle()

angular - 如何更改 Angular Material 日期选择器中的日期格式?

javascript - 单元测试错误 : Cannot call Promise. 然后来自同步测试

javascript - 尝试使用 *ngFor 循环显示数组

javascript - JS 文件中的 TypeScript 语法 - 可能吗?

typescript - 使用reflect-metadata获取返回类型,当返回类型是某物的 promise 时

javascript - mat-sidenav-container 在 dom 中始终为空