web-dev-qa-db-ja.com

Angular Material-複数のフィルターと複数のフィルター値で同時にテーブルをフィルターする

オブジェクトのテーブルに2つの異なるフィルターを設定できるようにしようとしています。最初のものは通常のテキスト/入力ベースのフィルターであり、期待どおりに機能します。

2番目に機能させようとしているのは、「レベル1」、「レベル2」などのラベルが付いたチェックボックスの行です。チェックボックスをオンにすると、現在チェックされているすべてのチェックボックスを「レベル」列でフィルタリングします。理想的には、ユーザーはテキストとレベルの選択の両方でテーブルをフィルタリングできます

FilterPredicateの使用について読み、 これをテンプレートとして を使用しようとしましたが、何かが足りないはずです。ポインタをいただければ幸いです。

現在のコードスニペット:

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」など)を検索できるようにし、チェックボックスでレベルのみをフィルター処理できるようにしたいと考えています(奇妙なリクエストですが、これは友達のために作成していますだから私は彼を助けようとしているだけです)。

現在のところ、チェックボックスをクリックすると、次のエラーが発生します。 'このコード行を指す' undefinedのプロパティ '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

ここで要求されているのは、テーブルの行の1つの例です:(これが友人の呪文の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;
  }
3
av0000

チェックボックスを設定し、チェックボックスのモデルを使用してcustomFilterPredicate関数でフィルタリングすることで正しい方向に進んでいました。

リンクされたstackblitzを取得し、チェックボックスで機能するように変更しました。チェックしてください ここ

テンプレートには、設定したようなチェックボックスがあります。

<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プロパティをフィルタリングし、2番目の条件ですべてのレベルが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);
}
5
Fabian Küng