web-dev-qa-db-fra.com

Angular Matériau - Table de filtre par plusieurs filtres et plusieurs valeurs de filtrage en même temps

J'essaie de pouvoir avoir deux filtres différents pour une table d'objets. Le premier est un filtre à base de texte/entrée normal et fonctionne comme prévu.

La seconde que j'essaie de travailler est une ligne de cases à cocher étiquetées "Niveau 1", "Niveau 2", etc. Lors de la cocher Une case à cocher Je veux filtrer par la colonne "Niveau" Toutes les cases à cocher actuellement cochées. Idéalement, l'utilisateur pourrait filtrer la table par la sélection de texte et de niveau.

J'ai lu sur l'utilisation des filtres et essayé d'utiliser ceci comme modèle mais je dois manquer quelque chose.

Code actuel Snippet:

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

Mettre à jour

Jusqu'à présent, j'ai pu faire des progrès grâce à Fabian Küng, mais je ne suis toujours pas libre à la maison. Pour clarifier, j'espère que le filtre de texte peut être capable de rechercher plusieurs colonnes ("CastingTime", "Durée", etc.), et que les cases à cocher ne font que filtrer par niveau.

À l'heure actuelle, lorsque je clique sur une case à cocher, j'obtiens cette erreur: "Impossible de lire la propriété 'Tolowercase' de indéfinie" qui pointe sur cette ligne de code:

return data['level'].toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1 &&

mais lorsque je console déconnecte des données, je peux voir qu'il a une propriété de niveau, donc je ne vois pas où je vais mal.

Voici les extraits de code pertinents:

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

Mise à jour II

Comme demandé ici est un exemple de l'une des lignes de table: (Ceci est pour une table de sorts 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"
}

Mise à jour finale

C'est ce que j'ai fini au cas où quelqu'un est coincé. Merci à Fabian Küng de figurer tout cela! La seule chose étrange de la note est que j'ai fini par utiliser la valeur réelle de la saisie de texte "Textfilter" car strayifiez le texte filtréValues, puis les analysant (le filtre n'accepte que des chaînes aussi loin que je peux dire) Conservez-moi une erreur? , Valeur inattendue à 0 "Messages et autant que je puisse dire que cela fonctionnera bien, sauf que je dois comprendre comment manifester le filtre.

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;
  }
5
av0000

Vous étiez sur la bonne voie avec la configuration des cases à cocher, puis vous utilisez le modèle des cases à cocher pour filtrer dans la fonction customFilterPredicate.

J'ai pris le lien Stackblitz et l'a modifié pour le faire fonctionner avec vos cases à cocher, le vérifier ici .

Dans le modèle, vous avez vos cases à cocher comme vous le configurez:

<mat-checkbox (change)="updateFilter()" [(ngModel)]="level.active" *ngFor="let level of levelsToShow">{{level.name}}</mat-checkbox>

Dans le composant, j'ai ajouté une interface Level:

export interface Level {
  active: boolean;
  name: string;
  level: number;
}

et certaines données:

levelsToShow: Level[] = [
  { level: 1, active: false, name: 'Level 1' },
  { level: 2, active: false, name: 'Level 2' },
  { level: 3, active: false, name: 'Level 3' },
];

Et la partie la plus importante est dans la fonction customFilterPredicate. Ce qu'il fait, c'est, cela filtre la propriété name dans la première condition et dans la seconde, il vérifie si tous les niveaux sont définis sur false, dans ce cas, nous retournons true Comme je suppose que vous voulez voir tous les niveaux si aucune case n'est cochée. Si un niveau est actif, nous filtrons par ce niveau, ou plusieurs niveaux si plusieurs sont sélectionnés.

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

Une pièce petite mais importante consiste à déclencher le filtrage lorsque vous vérifiez/décochez une case à cocher. Cela peut être fait en définissant simplement le DataSource filter à nouveau et est appelé chaque fois qu'une valeur de la case à cocher change:

updateFilter() {
  this.dataSource.filter = JSON.stringify(this.filteredValues);
}
5
Fabian Küng

Vous pouvez utiliser MAT-TABLE-FILTER pour vos exigences de filtrage compliquées si vous utilisez @angular/material.

Vérifiez cela Réponse

0
talhature