Как применить фильтры к * ngFor?
По-видимому, Angular 2 будет использовать каналы вместо фильтров, как в Angular1 в сочетании с ng-for, для фильтрации результатов, хотя реализация все еще кажется расплывчатой, без четкой документации.
А именно то, что я пытаюсь достичь, может быть рассмотрено со следующей точки зрения
<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>
Как это реализовать, используя трубы?
Ответы
Ответ 1
По сути, вы пишете канал, который затем можете использовать в директиве *ngFor
.
В вашем компоненте:
filterargs = {title: 'hello'};
items = [{title: 'hello world'}, {title: 'hello kitty'}, {title: 'foo bar'}];
В своем шаблоне вы можете передать строку, число или объект в ваш канал, чтобы использовать его для фильтрации:
<li *ngFor="let item of items | myfilter:filterargs">
В твоей трубе:
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);
}
}
Не забудьте зарегистрировать свой канал в app.module.ts
; вам больше не нужно регистрировать каналы в вашем @Component
import { MyFilterPipe } from './shared/pipes/my-filter.pipe';
@NgModule({
imports: [
..
],
declarations: [
MyFilterPipe,
],
providers: [
..
],
bootstrap: [AppComponent]
})
export class AppModule { }
Здесь Plunker, который демонстрирует использование настраиваемой фильтрующей трубы и встроенной трубы среза для ограничения результатов.
Обратите внимание (как отметили несколько комментаторов), что есть причина, по которой в Angular нет встроенных фильтровальных трубок.
Ответ 2
У многих из вас есть отличные подходы, но цель здесь состоит в том, чтобы быть общим и определить массив, который чрезвычайно многократно используется во всех случаях в отношении к * ngFor.
callback.pipe.ts (не забудьте добавить это в массив объявлений модуля)
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));
}
}
Затем в вашем компоненте вам необходимо реализовать метод со следующей сигнатурой (item: any) = > boolean, в моем случае, например, я назвал его filterUser, который фильтрует возраст пользователей которые составляют более 18 лет.
Ваш компонент
@Component({
....
})
export class UsersComponent {
filterUser(user: IUser) {
return !user.age >= 18
}
}
И последнее, но не менее важное: ваш html-код будет выглядеть так:
Ваш HTML
<li *ngFor="let user of users | callback: filterUser">{{user.name}}</li>
Как вы можете видеть, эта труба довольно универсальна во всех массивах, таких как элементы, которые необходимо фильтровать через обратный вызов. В моем случае я нашел, что это очень полезно для сценариев * ngFor.
Надеюсь, это поможет!!!
codematrix
Ответ 3
Упрощенный способ (используется только для небольших массивов из-за проблем с производительностью. В больших массивах необходимо выполнить фильтр вручную с помощью кода):
Смотрите: https://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);
}
}
Использование:
<li *ngFor="let it of its | filter : 'name' : 'value or variable'">{{it}}</li>
Если вы используете переменную в качестве второго аргумента, не используйте кавычки.
Ответ 4
Это то, что я реализовал без использования протокола.
component.html
<div *ngFor="let item of filter(itemsList)">
component.ts
@Component({
....
})
export class YourComponent {
filter(itemList: yourItemType[]): yourItemType[] {
let result: yourItemType[] = [];
//your filter logic here
...
...
return result;
}
}
Ответ 5
Я не уверен, когда это вошло, но они уже сделали трубу среза, которая сделает это. Это хорошо документировано тоже.
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>
Ответ 6
Вы также можете использовать следующее:
<template ngFor let-item [ngForOf]="itemsList">
<div *ng-if="conditon(item)"></div>
</template>
Это отобразит только div, если ваши элементы соответствуют условию
Дополнительную информацию см. в документации angular.
Если вам также понадобится индекс, используйте следующее:
<template ngFor let-item [ngForOf]="itemsList" let-i="index">
<div *ng-if="conditon(item, i)"></div>
</template>
Ответ 7
Трубы в Angular2 похожи на трубы в командной строке. Выход каждого предшествующего значения подается в фильтр после трубы, что упрощает процесс цепных фильтров:
<template *ngFor="#item of itemsList">
<div *ngIf="conditon(item)">{item | filter1 | filter2}</div>
</template>
Ответ 8
Я создал плункер на основе ответов здесь и в других местах.
Кроме того, мне пришлось добавить @Input
, @ViewChild
и ElementRef
из <input>
и создать и subscribe()
к наблюдаемой из них.
Фильтр поиска Angular2: PLUNKR (ОБНОВЛЕНИЕ: плункер больше не работает)
Ответ 9
Для этого требования я реализую и публикую общий компонент. Увидеть
https://www.npmjs.com/package/w-ng5
Для использования этих компонентов, прежде чем установить этот пакет с npm:
npm install w-ng5 --save
После этого импортируйте модуль в app.module
...
import { PipesModule } from 'w-ng5';
На следующем шаге добавьте раздел объявлений в app.module:
imports: [
PipesModule,
...
]
Образец использования
Фильтрация простой строки
<input type="text" [(ngModel)]="filtroString">
<ul>
<li *ngFor="let s of getStrings() | filter:filtroString">
{{s}}
</li>
</ul>
Фильтрация сложной строки - поле "Значение" на уровне 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>
Фильтрация сложной строки - среднее поле - "Значение" на уровне 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>
Фильтрация сложного массива простая - поле 'Ном' уровня 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>
Фильтрация в полях дерева - поле "Доблесть" на уровне 2 или "Доблесть" на уровне 1 или "Ном" на уровне 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>
Фильтрация несуществующего поля - "Доблесть" на несуществующем уровне 3
<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>
Этот компонент работает с бесконечным уровнем атрибута...
Ответ 10
Труба будет лучшим подходом. но ниже один также будет работать.
<div *ng-for="#item of itemsList">
<ng-container *ng-if="conditon(item)">
// my code
</ng-container>
</div>
Ответ 11
Другим подходом, который я хотел бы использовать для конкретных приложений, является использование специального свойства для чтения на вашем компоненте, которое позволяет вам инкапсулировать логику фильтрации более чисто, чем используя пользовательский канал (IMHO).
Например, если я хочу привязываться к albumList
и фильтровать на 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;
}
Чтобы связать в HTML, вы можете привязать к свойству только для чтения:
<a class="list-group-item"
*ngFor="let album of filteredAlbumList">
</a>
Я обнаружил, что для специализированных фильтров, специфичных для приложения, это работает лучше, чем канал, поскольку он поддерживает логику, связанную с фильтром, с компонентом.
Трубы лучше работают для фильтров многократного использования.
Ответ 12
Я создал следующий канал для получения желаемых элементов из списка.
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);
}
}
Преобразование в нижнем регистре просто подходит для случая нечувствительным способом.
Вы можете использовать его в своем представлении следующим образом: -
<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>
Ответ 13
В идеале для этого вам необходимо создать angualr 2 pipe. Но вы можете сделать этот трюк.
<ng-container *ngFor="item in itemsList">
<div*ngIf="conditon(item)">{{item}}</div>
</ng-container>
Ответ 14
Простое решение, которое работает с Angular 6 для фильтрации ngFor, это следующее:
<span *ngFor="item of itemsList" >
<div *ngIf="yourCondition(item)">
your code
</div>
</span
Ответ 15
Я знаю, что это старый вопрос, однако я подумал, что было бы полезно предложить другое решение.
эквивалент AngularJS этого
<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>
в Angular 2+ вы не можете использовать * ngFor и * ngIf для одного и того же элемента, поэтому он будет следующим:
<div *ngFor="let item of itemsList">
<div *ngIf="conditon(item)">
</div>
</div>
и если вы не можете использовать в качестве внутреннего контейнера, используйте вместо этого ng-container. ng-container полезен, когда вы хотите условно добавить группу элементов (то есть, используя * ngIf = "foo") в вашем приложении, но не хотите заключать их в другой элемент.
Ответ 16
Это мой код:
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);
}
}
Образец:
LIST = [{id:1,name:'abc'},{id:2,name:'cba'}];
FilterValue = 1;
<span *ngFor="let listItem of LIST | filter : 'id' : FilterValue">
{{listItem .name}}
</span>
Ответ 17
Вот пример, который я создал некоторое время назад, и о блоге, включающий рабочий план. Он предоставляет фильтр, который может фильтровать любой список объектов. В основном вы указываете свойство и значение {ключ: значение} в своей спецификации ngFor.
Он не сильно отличается от ответа @NateMay, за исключением того, что я объясняю его в довольно подробных подробностях.
В моем случае я отфильтровал неупорядоченный список в некотором тексте (filterText), введенный пользователем против свойства label в объектах моего массива с такой разметкой:
<ul>
<li *ngFor="let item of _items | filter:{label: filterText}">{{ item.label }}</li>
</ul>
https://long2know.com/2016/11/angular2-filter-pipes/
Ответ 18
Основываясь на очень изящном решении трубы обратного вызова, предложенном выше, его можно еще немного обобщить, разрешив передавать дополнительные параметры фильтра. Тогда имеем:
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));
}
}
компонент
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>
Ответ 19
Первый шаг, который вы создаете для фильтра с помощью @Pipe
в файле 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
}
})
И структура данных объекта Person:
person.ts
export class Person{
constructor(
public firstName: string,
public lastName: string
) { }
}
В вашем представлении в 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>
Ответ 20
Это ваш массив
products: any = [
{
"name": "John-Cena",
},
{
"name": "Brock-Lensar",
}
];
Это ваш цикл ngFor.
<input type="text" [(ngModel)]='filterText' />
<ul *ngFor='let product of filterProduct'>
<li>{{product.name }}</li>
</ul>
Там я использую filterProduct мгновенных продуктов, потому что я хочу сохранить свои исходные данные. Здесь модель _filterText используется в качестве поля ввода. Когда-либо будет вызываться любая функция установки изменений. В setFilterText executeProduct вызывается, он будет возвращать результат только тем, кто соответствует вводу. Я использую строчные буквы без учета регистра.
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);
}
Ответ 21
После некоторого ng2-search-filter
, я наткнулся на ng2-search-filter
. Он возьмет ваш объект и применит условие поиска ко всем свойствам объекта, которые ищут совпадение.
Ответ 22
Я трачу так много времени на поиск идеального решения :) и вот оно, спасибо, ребята!
Ответ 23
Я нашел кое-что для создания фильтра, передающего Объект, затем я могу использовать его как мульти-фильтр: ![Multi filter example]()
я сделал это решение для красоты:
filter.pipe.ts
import { PipeTransform, Pipe } from '@angular/core';
@Pipe({
name: 'filterx',
pure: false
})
export class FilterPipe implements PipeTransform {
transform(items: any, filter: any, isAnd: boolean): any {
let filterx=JSON.parse(JSON.stringify(filter));
for (var prop in filterx) {
if (Object.prototype.hasOwnProperty.call(filterx, prop)) {
if(filterx[prop]=='')
{
delete filterx[prop];
}
}
}
if (!items || !filterx) {
return items;
}
return items.filter(function(obj) {
return Object.keys(filterx).every(function(c) {
return obj[c].toLowerCase().indexOf(filterx[c].toLowerCase()) !== -1
});
});
}
}
component.ts
slotFilter:any={start:'',practitionerCodeDisplay:'',practitionerName:''};
componet.html
<tr>
<th class="text-center"> <input type="text" [(ngModel)]="slotFilter.start"></th>
<th class="text-center"><input type="text" [(ngModel)]="slotFilter.practitionerCodeDisplay"></th>
<th class="text-left"><input type="text" [(ngModel)]="slotFilter.practitionerName"></th>
<th></th>
</tr>
<tbody *ngFor="let item of practionerRoleList | filterx: slotFilter">...