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.
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>
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);
}
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>
J'ai trouvé que la solution la plus simple était la suivante:
filterMetadata = { count: 0 };
<tr *ngFor="#d of data.results | filter:filterText:filterMetadata | pagination:resultsPerPage:currentPage">
<span> {{filterMetadata.count}} records displayed</span>
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.
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.
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.
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.