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

时间:2019-01-26 05:10:02

标签: 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;
  }

更新

到目前为止,多亏了FabianKü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);
  }

更新II

以下是所请求的表行之一的示例: (以防万一这有助于为朋友建立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"
}

*最终更新* 如果有人被卡住,这就是我最终得到的结果。感谢FabianKü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;
  }

2 个答案:

答案 0 :(得分:1)

通过设置复选框,然后使用复选框的模型在customFilterPredicate函数中进行过滤,您走在正确的轨道上。

  

我接受了链接的stackblitz并对其进行了修改,以使其与您的   复选框,选中它   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,在这种情况下,我们假设返回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);
}

答案 1 :(得分:0)

如果使用@angular/material,则可以使用mat-table-filter满足复杂的过滤要求。

选中此answer