Как применить фильтры к * 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">...