angular - example - ¿Cómo aplicar filtros a*ngFor?
ngfor filter angular 4 (21)
Aparentemente, Angular 2 usará tuberías en lugar de filtros como en Angular1 junto con ng-for para filtrar resultados, aunque la implementación aún parece ser vaga, sin documentación clara.
Es decir, lo que estoy tratando de lograr podría verse desde la siguiente perspectiva
<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>
¿Cómo implementarlo usando tuberías?
Aquí hay un ejemplo que creé hace un tiempo, y en el que escribí en un blog, que incluye un golpe de trabajo. Proporciona una tubería de filtro que puede filtrar cualquier lista de objetos. Básicamente, solo especifica la propiedad y el valor {clave: valor} dentro de su especificación ngFor.
No es muy diferente de la respuesta de @ NateMay, excepto que lo explico con un detalle relativamente detallado.
En mi caso, filtré una lista desordenada en algún texto (filterText) que el usuario ingresó contra la propiedad "etiqueta" de los objetos en mi matriz con este tipo de marcado:
<ul>
<li *ngFor="let item of _items | filter:{label: filterText}">{{ item.label }}</li>
</ul>
Básicamente, usted escribe una tubería que luego puede usar en la directiva
*ngFor
.
En su componente:
filterargs = {title: ''hello''};
items = [{title: ''hello world''}, {title: ''hello kitty''}, {title: ''foo bar''}];
En su plantilla, puede pasar cadena, número u objeto a su tubería para usar para filtrar:
<li *ngFor="let item of items | myfilter:filterargs">
En tu pipa:
import { Pipe, PipeTransform } from ''@angular/core'';
@Pipe({
name: ''myfilter'',
pure: false
})
export class MyFilterPipe implements PipeTransform {
transform(items: any[], filter: Object): any {
if (!items || !filter) {
return items;
}
// filter items array, items which match and return true will be
// kept, false will be filtered out
return items.filter(item => item.title.indexOf(filter.title) !== -1);
}
}
Recuerde registrar su canalización en
app.module.ts
;
ya no necesita registrar las tuberías en su
@Component
import { MyFilterPipe } from ''./shared/pipes/my-filter.pipe'';
@NgModule({
imports: [
..
],
declarations: [
MyFilterPipe,
],
providers: [
..
],
bootstrap: [AppComponent]
})
export class AppModule { }
Aquí hay un Plunker que muestra el uso de una tubería de filtro personalizada y la tubería de corte incorporada para limitar los resultados.
Tenga en cuenta (como han señalado varios comentaristas) que angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe cual no hay tuberías de filtro integradas en Angular.
Basado en la solución de tubería de devolución de llamada muy elegante propuesta anteriormente, es posible generalizarlo un poco más al permitir que se pasen parámetros de filtro adicionales. Entonces tenemos:
callback.pipe.ts
import { Pipe, PipeTransform } from ''@angular/core'';
@Pipe({
name: ''callback'',
pure: false
})
export class CallbackPipe implements PipeTransform {
transform(items: any[], callback: (item: any, callbackArgs?: any[]) => boolean, callbackArgs?: any[]): any {
if (!items || !callback) {
return items;
}
return items.filter(item => callback(item, callbackArgs));
}
}
componente
filterSomething(something: Something, filterArgs: any[]) {
const firstArg = filterArgs[0];
const secondArg = filterArgs[1];
...
return <some condition based on something, firstArg, secondArg, etc.>;
}
html
<li *ngFor="let s of somethings | callback : filterSomething : [<whatWillBecomeFirstArg>, <whatWillBecomeSecondArg>, ...]">
{{s.aProperty}}
</li>
Creé la siguiente tubería para obtener los elementos deseados de una lista.
import { Pipe, PipeTransform } from ''@angular/core'';
@Pipe({
name: ''filter''
})
export class FilterPipe implements PipeTransform {
transform(items: any[], filter: string): any {
if(!items || !filter) {
return items;
}
// To search values only of "name" variable of your object(item)
//return items.filter(item => item.name.toLowerCase().indexOf(filter.toLowerCase()) !== -1);
// To search in values of every variable of your object(item)
return items.filter(item => JSON.stringify(item).toLowerCase().indexOf(filter.toLowerCase()) !== -1);
}
}
La conversión en minúsculas es solo para que coincida en mayúsculas y minúsculas. Puede usarlo en su vista así: -
<div>
<input type="text" placeholder="Search reward" [(ngModel)]="searchTerm">
</div>
<div>
<ul>
<li *ngFor="let reward of rewardList | filter:searchTerm">
<div>
<img [src]="reward.imageUrl"/>
<p>{{reward.name}}</p>
</div>
</li>
</ul>
</div>
Después
ng2-search-filter
Google, me encontré con
ng2-search-filter
.
In tomará su objeto y aplicará el término de búsqueda contra todas las propiedades del objeto en busca de una coincidencia.
El primer paso para crear un filtro con
@Pipe
en su archivo component.ts:
your.component.ts
import { Component, Pipe, PipeTransform, Injectable } from ''@angular/core'';
import { Person} from "yourPath";
@Pipe({
name: ''searchfilter''
})
@Injectable()
export class SearchFilterPipe implements PipeTransform {
transform(items: Person[], value: string): any[] {
if (!items || !value) {
return items;
}
console.log("your search token = "+value);
return items.filter(e => e.firstName.toLowerCase().includes(value.toLocaleLowerCase()));
}
}
@Component({
....
persons;
ngOnInit() {
//inicial persons arrays
}
})
Y estructura de datos del objeto Persona:
person.ts
export class Person{
constructor(
public firstName: string,
public lastName: string
) { }
}
En su vista en el archivo html:
your.component.html
<input class="form-control" placeholder="Search" id="search" type="text" [(ngModel)]="searchText"/>
<table class="table table-striped table-hover">
<colgroup>
<col span="1" style="width: 50%;">
<col span="1" style="width: 50%;">
</colgroup>
<thead>
<tr>
<th>First name</th>
<th>Last name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let person of persons | searchfilter:searchText">
<td>{{person.firstName}}</td>
<td>{{person.lastName}}</td>
</tr>
</tbody>
</table>
Esta es tu matriz
products: any = [
{
"name": "John-Cena",
},
{
"name": "Brock-Lensar",
}
];
Este es tu ngFor loop Filtrar por:
<input type="text" [(ngModel)]=''filterText'' />
<ul *ngFor=''let product of filterProduct''>
<li>{{product.name }}</li>
</ul>
Ahí estoy usando filterProduct instantáneo de productos, porque quiero preservar mis datos originales. Aquí el modelo _filterText se usa como un cuadro de entrada. Cuando haya alguna función de cambio, se llamará. En setFilterText se llama a performProduct, devolverá el resultado solo a aquellos que coincidan con la entrada. Estoy usando minúsculas para mayúsculas y minúsculas.
filterProduct = this.products;
_filterText : string;
get filterText() : string {
return this._filterText;
}
set filterText(value : string) {
this._filterText = value;
this.filterProduct = this._filterText ? this.performProduct(this._filterText) : this.products;
}
performProduct(value : string ) : any {
value = value.toLocaleLowerCase();
return this.products.filter(( products : any ) =>
products.name.toLocaleLowerCase().indexOf(value) !== -1);
}
Este es mi código:
import {Pipe, PipeTransform, Injectable} from ''@angular/core'';
@Pipe({
name: ''filter''
})
@Injectable()
export class FilterPipe implements PipeTransform {
transform(items: any[], field : string, value): any[] {
if (!items) return [];
if (!value || value.length === 0) return items;
return items.filter(it =>
it[field] === value);
}
}
Muestra:
LIST = [{id:1,name:''abc''},{id:2,name:''cba''}];
FilterValue = 1;
<span *ngFor="let listItem of LIST | filter : ''id'' : FilterValue">
{{listItem .name}}
</span>
Esto es lo que implementé sin usar pipe.
componente.html
<div *ngFor="let item of filter(itemsList)">
componente.ts
@Component({
....
})
export class YourComponent {
filter(itemList: yourItemType[]): yourItemType[] {
let result: yourItemType[] = [];
//your filter logic here
...
...
return result;
}
}
Forma simplificada (se usa solo en matrices pequeñas debido a problemas de rendimiento. En matrices grandes debe hacer el filtro manualmente a través del código):
Ver: angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe
@Pipe({
name: ''filter''
})
@Injectable()
export class FilterPipe implements PipeTransform {
transform(items: any[], field : string, value : string): any[] {
if (!items) return [];
if (!value || value.length == 0) return items;
return items.filter(it =>
it[field].toLowerCase().indexOf(value.toLowerCase()) !=-1);
}
}
Uso:
<li *ngFor="let it of its | filter : ''name'' : ''value or variable''">{{it}}</li>
Si usa una variable como segundo argumento, no use comillas.
He creado un plunker basado en las respuestas aquí y en otros lugares.
Además, tuve que agregar un
@Input
,
@ViewChild
y
ElementRef
de
<input>
y crear y
subscribe()
a un observable de él.
Filtro de búsqueda Angular2: PLUNKR (ACTUALIZACIÓN: plunker ya no funciona)
Idealmente, debe crear una tubería angualr 2 para eso. Pero puedes hacer este truco.
<ng-container *ngFor="item in itemsList">
<div*ngIf="conditon(item)">{{item}}</div>
</ng-container>
La tubería sería el mejor enfoque. pero debajo de uno también funcionaría.
<div *ng-for="#item of itemsList">
<ng-container *ng-if="conditon(item)">
// my code
</ng-container>
</div>
Las tuberías en Angular2 son similares a las tuberías en la línea de comando. La salida de cada valor anterior se introduce en el filtro después de la tubería, lo que facilita el encadenamiento de filtros así:
<template *ngFor="#item of itemsList">
<div *ngIf="conditon(item)">{item | filter1 | filter2}</div>
</template>
Muchos de ustedes tienen excelentes enfoques, pero el objetivo aquí es ser genérico y definir una tubería de matriz que sea extremadamente reutilizable en todos los casos en relación con * ngFor.
callback.pipe.ts (no olvide agregar esto a la matriz de declaración de su módulo)
import { PipeTransform, Pipe } from ''@angular/core'';
@Pipe({
name: ''callback'',
pure: false
})
export class CallbackPipe implements PipeTransform {
transform(items: any[], callback: (item: any) => boolean): any {
if (!items || !callback) {
return items;
}
return items.filter(item => callback(item));
}
}
Luego, en su componente, debe implementar un método con la siguiente firma (ítem: any) => boolean , en mi caso, por ejemplo, lo llamé filterUser, que filtra la edad de los usuarios que es mayor de 18 años.
Su componente
@Component({
....
})
export class UsersComponent {
filterUser(user: IUser) {
return !user.age >= 18
}
}
Y por último, pero no menos importante, su código html se verá así:
Su HTML
<li *ngFor="let user of users | callback: filterUser">{{user.name}}</li>
Como puede ver, este Pipe es bastante genérico en todos los elementos similares a la matriz que deben filtrarse mediante una devolución de llamada. En mi caso, me pareció muy útil para * ng Para escenarios similares.
¡¡¡Espero que esto ayude!!!
codematrix
No estoy seguro de cuándo llegó, pero ya hicieron un tubo de corte que lo hará. También está bien documentado.
https://angular.io/docs/ts/latest/api/common/index/SlicePipe-pipe.html
<p *ngFor="let feature of content?.keyFeatures | slice:1:5">
{{ feature.description }}
</p>
Otro enfoque que me gusta usar para los filtros específicos de la aplicación es usar una propiedad personalizada de solo lectura en su componente que le permita encapsular la lógica de filtrado de manera más limpia que usar una tubería personalizada (IMHO).
Por ejemplo, si quiero enlazar a
albumList
y filtrar en
searchText
:
searchText: "";
albumList: Album[] = [];
get filteredAlbumList() {
if (this.config.searchText && this.config.searchText.length > 1) {
var lsearchText = this.config.searchText.toLowerCase();
return this.albumList.filter((a) =>
a.Title.toLowerCase().includes(lsearchText) ||
a.Artist.ArtistName.toLowerCase().includes(lsearchText)
);
}
return this.albumList;
}
Para enlazar el HTML, puede enlazar a la propiedad de solo lectura:
<a class="list-group-item"
*ngFor="let album of filteredAlbumList">
</a>
Creo que para los filtros especializados que son específicos de la aplicación, esto funciona mejor que una tubería, ya que mantiene la lógica relacionada con el filtro con el componente.
Las tuberías funcionan mejor para filtros reutilizables a nivel mundial.
Para este requisito, implemento y publico un componente genérico . Ver
https://www.npmjs.com/package/w-ng5
Para usar estos componentes, antes, instale este paquete con npm:
npm install w-ng5 --save
Después, importe el módulo en app.module
...
import { PipesModule } from ''w-ng5'';
En el siguiente paso, agregue en la sección declarar de app.module:
imports: [
PipesModule,
...
]
Uso de la muestra
Filtrar cadenas simples
<input type="text" [(ngModel)]="filtroString">
<ul>
<li *ngFor="let s of getStrings() | filter:filtroString">
{{s}}
</li>
</ul>
Filtrado de cadenas complejas: campo ''Valor'' en el nivel 2
<input type="text" [(ngModel)]="search">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:''n1.n2.valor2'', value: search}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Filtrado de cadenas complejas - campo medio - ''Valor'' en el nivel 1
<input type="text" [(ngModel)]="search3">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:''n1.valor1'', value: search3}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Filtrado de matriz compleja simple - campo ''Nome'' nivel 0
<input type="text" [(ngModel)]="search2">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:''nome'', value: search2}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Filtrado en campos de árbol: campo ''Valor'' en el nivel 2 o ''Valor'' en el nivel 1 o ''Nome'' en el nivel 0
<input type="text" [(ngModel)]="search5">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:''n1.n2.valor2'', value: search5}, {field:''n1.valor1'', value: search5}, {field:''nome'', value: search5}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Filtrado de campo inexistente - ''Valor'' en nivel 3 inexistente
<input type="text" [(ngModel)]="search4">
<ul>
<li *ngFor="let s of getComplexTypesExtends() | filter:[{field:''n1.n2.n3.valor3'', value: search4}]">
{{s.nome}} - {{s.idade}} - {{s.n1.valor1}} - {{s.n1.n2.valor2}}
</li>
</ul>
Este componente funciona con un nivel de atributo infinito ...
Sé que es una vieja pregunta, sin embargo, pensé que podría ser útil ofrecer otra solución.
equivalente de AngularJS de esto
<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>
en Angular 2+ no puede usar * ngFor y * ngIf en un mismo elemento, por lo que será el siguiente:
<div *ngFor="let item of itemsList">
<div *ngIf="conditon(item)">
</div>
</div>
y si no puede usarlo como contenedor interno, use ng-container en su lugar. ng-container es útil cuando desea agregar condicionalmente un grupo de elementos (es decir, usar * ngIf = "foo") en su aplicación pero no quiere envolverlos con otro elemento.
También puede usar lo siguiente:
<template ngFor let-item [ngForOf]="itemsList">
<div *ng-if="conditon(item)"></div>
</template>
Esto solo mostrará el div si sus artículos coinciden con la condición
Consulte la documentación angular para obtener más información. Si también necesita el índice, utilice lo siguiente:
<template ngFor let-item [ngForOf]="itemsList" let-i="index">
<div *ng-if="conditon(item, i)"></div>
</template>
Una solución simple que funciona con Angular 6 para filtrar un ngFor, es la siguiente:
<span *ngFor="item of itemsList" >
<div *ngIf="yourCondition(item)">
your code
</div>
</span
Los tramos son útiles porque no representan inherentemente nada.