web-dev-qa-db-fra.com

Comment trouver le nombre d'éléments dans un ngFor après l'application des tuyaux

J'ai un ngFor pour créer des lignes dans une table qui est à la fois filtrée et paginée.

<tr *ngFor="#d of data.results | filter:filterText | pagination:resultsPerPage:currentPage">

Il y a un autre élément sur la page qui affiche le nombre d'enregistrements affichés. Ces éléments sont initialement liés aux données. Longueur des résultats.

Comment puis-je obtenir la longueur des données affichées après l'application du tuyau de filtre afin de pouvoir l'afficher correctement. Aucune des variables locales fournies dans ngFor ne semble expliquer cela.

21
ghawkes

Vous pouvez obtenir le nombre d'éléments en transformant le tableau dans un tuyau.

L'idée est que le tube transformerait le tableau en un autre tableau où chaque élément a une propriété d'élément et une propriété parent représentant le tableau filtré (ou original):

@Pipe({ name: 'withParent', pure: false })
export class WithParentPipe implements PipeTransform {
    transform(value: Array<any>, args: any[] = null): any {

        return value.map(t=> {
            return {
                item: t,
                parent: value
            }
        });
    }
} 

Voici comment il serait utilisé:

 <tr *ngFor="#d of data.results | 
        filter:filterText |
        pagination:resultsPerPage:currentPage | 
        withParent">
        Count:  {{d.parent.length }}
        Item:  {{ d.item.name}}
 </tr>
7
pixelbits

Une façon consiste à utiliser des variables de modèle avec @ViewChildren()

<tr #myVar *ngFor="let d of data.results | filter:filterText | pagination:resultsPerPage:currentPage">
@ViewChildren('myVar') createdItems;

ngAfterViewInit() {
  console.log(this.createdItems.toArray().length);
}
14
Günter Zöchbauer

Ce n'est pas exactement le but de la question d'origine, mais je cherchais également un moyen d'afficher le nombre d'articles une fois que tous les tuyaux ont été appliqués. En combinant l'index et les dernières valeurs fournies par ngFor, j'ai trouvé cette autre solution:

<div *ngFor="#item of (items | filter); #i = index; #last = last">
...
  <div id="counter_id" *ngIf="last">{{index + 1}} results</div>
</div>
8
jean-baptiste

J'ai trouvé que la solution la plus simple était la suivante:

  1. Dans votre composant: ajoutez un champ qui contiendra le nombre actuel
  filterMetadata = { count: 0 };
  1. Dans votre modèle: ajoutez le filterMetadata en tant que paramètre au tuyau de filtre
  <tr *ngFor="#d of data.results | filter:filterText:filterMetadata | pagination:resultsPerPage:currentPage">
  1. interpoler filterMetadata.count dans l'élément qui affiche le nombre d'enregistrements affichés.
  <span> {{filterMetadata.count}} records displayed</span>
  1. Dans le canal de filtrage, mettez à jour le champ filterMetadata.count lorsque vous avez terminé avec le filtrage
  transform(items, ..., filterMetadata) {
    // filtering
    let filteredItems = filter(items);

    filterMetadata.count = filteredItems.length;
    return filteredItems;
  }

Cette solution utilise toujours des tuyaux purs, il n'y a donc aucune dégradation des performances. Si vous avez plusieurs canaux qui filtrent, le filterMetadata doit être ajouté en tant que paramètre à tous parce que angular arrête d'appeler la séquence de canaux dès que le canal a renvoie un tableau vide, donc vous ne peut pas simplement l'ajouter au premier ou au dernier tuyau de filtrage.

6
Soufiane Sakhi

J'ai rencontré le même problème, bien que la réponse de @bixelbits ait été approuvée, mais je ne l'ai pas trouvée idéale, spécialement pour les données volumineuses.

Au lieu de retourner le tableau d'origine dans chaque élément, je pense qu'il vaut mieux éviter simplement Pipes pour ce problème, au moins avec l'implémentation actuelle Angular 2 (rc4).

Une meilleure solution serait d'utiliser la fonction d'un composant normal pour filtrer les données, quelque chose comme ci-dessous:

// mycomponent.component.ts  
filter() {
  let arr = this.data.filter(
      // just an example
      item => item.toLowerCase().includes(
        // term is a local variable I get it's from <input> 
        this.term.toLowerCase()
      )
    );
  this.filteredLength = arr.length;
  return arr;
}

Ensuite, dans le modèle:

<ul>
  <li *ngFor="let el of filter()"> 
    {{ el | json }}
  </li>
</ul>
// mycomponent.component.html
<p > There are {{ filteredLength }} element(s) in this page</p>

À moins que vous ne vouliez vraiment utiliser Pipes, je vous recommande de les éviter dans des situations comme l'exemple ci-dessus.

5
Ahmed T. Ali

J'ai donc trouvé une solution pour cela.

J'ai créé un tuyau qui prend une référence d'objet et met à jour une propriété avec le nombre passant actuellement par le tuyau.

@Pipe({
    name: 'count'
})

export class CountPipe implements PipeTransform {
    transform(value, args) {
        if (!args) {
            return value;
        }else if (typeof args === "object"){

            //Update specified object key with count being passed through
            args.object[args.key] = value.length;

            return value;

        }else{
            return value;
        }
    }
}

Ensuite, dans votre vue, associez un composant de pagination comme celui-ci.

pagination-controls(#controls="", [items]="items.length", (onChange)="o") 

tbody
    tr(*ngFor=`let item of items
        | filter_pipe: { .... }
        | count: { object: controls , key: 'items' }
        | pagination_pipe: { ... } `)

Une fois que cette propriété count est extraite du tube vers le composant actuel ou un composant enfant, vous pouvez faire quoi que ce soit avec.

4
Victor96

Dans mon cas, j'avais besoin de parcourir les éléments filtrés et d'exécuter une analyse.

Ma solution consiste simplement à passer une fonction et à renvoyer le tableau sur le dernier tuyau. Vous pouvez également créer un tuyau spécialement pour cela, mais je l'ai ajouté à mon dernier tuyau dans les filtres:

HTML

<divclass="card" *ngFor="let job of jobs | employee:jobFilter.selectedEmployee | managerStatus:jobFilter.selectedStatus | dateOrder:jobFilter"> 

Composant

this.jobFilter = {
  jobStatuses: {},  // status labels
  ordering: 'asc',
  selectedEmployee: {},
  selectedStatus: {}, // results status
  fn: this.parseFilteredArr
};

parseFilteredArr(arr) {
  console.log(arr);
}

Tuyau

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'dateOrder'
})
export class DateOrderPipe implements PipeTransform {
 transform(value: any, args?: any): any {
   const arr = Array.isArray(value)
    ? value.reverse()
    : value;
  args.fn(arr);
  return arr;
}
}

Comme vous pouvez le voir, j'ai appelé la fonction dans le tuyau

args.fn (arr);

et maintenant peut le traiter dans le contrôleur.

0
dale