Как применить фильтры к * ngFor?

278

По-видимому, Angular 2 будет использовать каналы вместо фильтров, как в Angular1 в сочетании с ng-for, для фильтрации результатов, хотя реализация все еще кажется расплывчатой, без четкой документации.

А именно то, что я пытаюсь достичь, можно рассматривать со следующей точки зрения

<div *ng-for="#item of itemsList" *ng-if="conditon(item)"></div>

Как это реализовать, используя трубы?

Халед
источник
8
Обратите внимание, что в бета-версии 17 для ngFor введено критическое изменение относительно символа хеша. Правильный путь:<div *ngFor="let item of itemsList" *ngIf="conditon(item)" ...
Memet Olsen
11
Комментарий @MemetOlsen от Гюнтера ниже: « *ngForи *ngIfдля одного и того же элемента не поддерживаются. Вам нужно перейти к явной форме для одного из них»
The Red Pea
1
Даже если это то, что запрашивает OP, рекомендуется НЕ ИСПОЛЬЗОВАТЬ ТРУБУ для фильтрации или упорядочивания в Angular2 +. Предпочитают иметь свойство класса с отфильтрованными значениями: angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe
ylerjen

Ответы:

395

По сути, вы пишете канал, который затем можете использовать в *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 нет встроенных фильтровальных трубок.

phuc77
источник
6
Спасибо, это работает как задумано, но иногда лучше проверить, определен ли массив элементов, а не ноль, потому что Ng2 может попытаться применить фильтр, в то время как «элементы» все еще не определены.
Тиммз
1
Кроме того, мне нужно было добавить класс фильтра в объявление @Component. Как и так: @Component ({... pipe: [MyFilterPipe]
Стивен
1
Я думаю, что она также нуждается в этой строке «f (! Items) return items;» в случае, если массив пуст.
Боштян Пишлер
2
Ангуляр говорит, что использование Pipe имеет проблемы с производительностью, поэтому рекомендует выполнить фильтрацию компонента
Себастьян Рохас,
3
Я хотел бы предложить *ngForзаключить параметры в скобки, чтобы избежать путаницы и сделать ее «защищенной от изменений»:<li *ngFor="let item of (items | myfilter:filterargs)">
Томас
104

У многих из вас есть отличные подходы, но цель здесь состоит в том, чтобы быть универсальным и определить канал массива, который чрезвычайно многократно используется во всех случаях по отношению к * 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>

Как видите, этот канал довольно универсален для всех массивов, таких как элементы, которые необходимо отфильтровать с помощью обратного вызова. В mycase я обнаружил, что это очень полезно для * ng для подобных сценариев.

Надеюсь это поможет!!!

codematrix

code5
источник
4
Я заметил, что в функции filterUser () - или моей эквивалентной ей функции - вы не можете использовать «this» для доступа к текущему экземпляру компонента, как вы можете со всеми другими функциями в классе компонента. Мне нужно получить доступ к объекту компонента, чтобы проверить, что отфильтрованный элемент находится в коллекции.
Пол
1
@ Пол, хм ... это невозможно. Ваш метод частный? Не это должно иметь значение, поскольку рядовые объекты являются только компиляционными конструкциями и не применяются во время выполнения. В моем примере я использовал IUser. Это предполагает, что элементы в итерируемой коллекции отображаются на нее. Вы можете попробовать любой, чтобы увидеть, работает ли он. Также убедитесь, что имя набрано правильно, регистр и все.
code5
1
я не могу получить доступ к переменной компонента, используя этот метод
suulisin
10
Чтобы избежать проблемы thisнеопределенности, вы можете написать свой метод на вашем компоненте, filterUser = (user: IUser) =>а неfilteruser(user: IUser)
Том
2
@ Пол, я знаю, что уже слишком поздно, чтобы помочь тебе, но это может помочь другим. Причина, по которой вы теряли thisметод компонента, заключается в том, что метод использовался в качестве обратного вызова и thisбыл применен новый контекст. Вы столкнулись с общей проблемой в объектно-ориентированном javascript, но есть старое и простое решение: вы привязываете методы, которые будут использоваться в качестве обратных вызовов к исходному классу. В свой конструктор добавьте следующий код: this.myCallbackFunc = this.myCallbackFunc.bind(this); Вот и все. Вы никогда не проиграете thisснова.
Рэндольфо
36

Упрощенный способ (используется только для небольших массивов из-за проблем с производительностью. В больших массивах необходимо выполнить фильтр вручную с помощью кода):

Смотрите: 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>

Если вы используете переменную в качестве второго аргумента, не используйте кавычки.

Родольфо Хорхе Немер Ногейра
источник
3
Возможно, добавьте следующее, чтобы показать, как его объединить с ReqExp: return items.filter (item => {return new RegExp (value, "i"). Test (item [field])});
Йоханнес
8
По словам команды Angular, это считается плохой практикой.
@torazaburo Вы можете сослаться на их мнение или объяснить, почему? Спасибо
Zymotik
2
По словам команды Angular, это плохой код, потому что он медленный и плохо продуман. Команда не хочет видеть медленные сайты из-за их кода, поэтому на этот раз они не встроили его в Angular. angular.io/docs/ts/latest/guide/…
Zymotik
29

Это то, что я реализовал без использования трубы.

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;
  }
}
Тханг Ле
источник
16
Я думаю, что это потребует больших вычислительных ресурсов, потому что Angular будет выполнять фильтр каждый раз, когда запускает обнаружение изменений. Это не будет хорошо масштабироваться для больших массивов. Очиститель, хотя и более сложный для кода, решение было бы сделать itemListнаблюдаемое и использовать асинхронную фильтр: let item of itemsList | async. Когда произойдет изменение, сделайте наблюдаемый выброс нового списка. Таким образом, фильтрующий код запускается только при необходимости.
BeetleJuice
1
Этот ответ должен иметь отрицательную оценку. Это плохо, используйте трубу.
Cétia
19

Я не уверен, когда это вошло, но они уже сделали трубу среза, которая сделает это. Это также хорошо задокументировано.

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>
SpaceBeers
источник
4
Если вы используете интерфейс trackBy, труба среза должна быть применена перед ;. Например:*ngFor="let feature of content?.keyFeatures | slice:1:5; trackBy feature?.id"
Филипп
11

Вы также можете использовать следующее:

<template ngFor let-item [ngForOf]="itemsList">
    <div *ng-if="conditon(item)"></div>
</template>

Это покажет div, только если ваши элементы соответствуют условию

См. Угловую документацию для получения дополнительной информации. Если вам также потребуется индекс, используйте следующее:

<template ngFor let-item [ngForOf]="itemsList" let-i="index">
    <div *ng-if="conditon(item, i)"></div>
</template>
Йерун
источник
1
Не будет ли это вводить шаблон для каждого элемента в списке, а не только для отфильтрованного списка? Это может быть ударом производительности.
Azeroth2b
8

трубы в Angular2 похожи на трубы в командной строке. Выход каждого предыдущего значения подается в фильтр после канала, что упрощает цепочку фильтров, например, так:

<template *ngFor="#item of itemsList">
    <div *ngIf="conditon(item)">{item | filter1 | filter2}</div>
</template>
Бен Глассер
источник
Извините, если это вводило в заблуждение, моя точка зрения здесь заключается в том, что переменная itemfrom *ng-for="#item of itemsList"должна использоваться для фильтрации результатов как таковых *ng-if="conditon(item)". Что не работает в этом примере ..
Халед
Вы можете сделать условие фильтра и сделать то же самое с {{item | условие}} условие будет просто возвращено, itemесли условие выполнено, и никакого значения, если оно не выполнено.
Бен Глассер
@BenGlasser Я думал, что трубы были применены справа налево. Таким образом, сначала будет применен filter2, а затем filter1.
Эван Плейс
12
*ngForи *ngIfна том же элементе не поддерживаются. Вам нужно перейти к явной форме для одного из них<template ngFor ...>
Günter Zöchbauer
1
@ GünterZöchbauer Это заняло у меня год, но я обновил синтаксис, чтобы отразить предложенные вами изменения
Бен Глассер,
5

Для этого требования я реализую и публикую общий компонент . Видеть

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>

Этот компонент работает с бесконечным уровнем атрибута ...

Уэдсон Кинтанилха да Силва
источник
Привет, я здесь, и я выполнил все шаги, и в этом случае я использую это, *ngFor="let inovice of invoices | filter:searchInvoice"и он ищет в моем списке, но показывает пустой список, вы знаете, почему?
Jecorrales
1
Здравствуйте, скажите, пожалуйста, какова структура и тип объектов, содержащихся в вашем списке счетов. То, как вы его используете, должно применяться только в том случае, если ваш список счетов имеет тип string. Если вы хотите выполнить поиск по номеру счета-фактуры (invoice.number), используйте это: * ngFor = "let inovice of invoices | filter: {field: number, value: searchInvoice}" . Если вы хотите фильтровать по двум столбцам, например, invoice.customer.name, используйте: * ngFor = "let inovice of invoices | filter: [field: number, value: searchInvoice}, {field: customer.name, value: searchInvoice}] .
Уэдсон Кинтанилья да Силва
4

Простое решение, которое работает с Angular 6 для фильтрации ngFor, это следующее:

<span *ngFor="item of itemsList"  >
  <div *ngIf="yourCondition(item)">
    
    your code
    
  </div>
</span

Пролеты полезны, потому что по своей сути ничего не представляют.

Майкл V
источник
1
Лучше, чем <span>, использовать <ng-container>, поскольку он не добавит ненужной разметки, которая в дополнение к шуму html может повлиять на ваш CSS.
Тревор де
4

Я знаю, что это старый вопрос, однако я подумал, что было бы полезно предложить другое решение.

эквивалент 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") в свое приложение, но не хотите заключать их в другой элемент.

tgralex
источник
4

Я создал плункер на основе ответов здесь и в других местах.

Кроме того , я должен был добавить @Input, @ViewChildи ElementRefиз <input>и создать и subscribe()к наблюдаемым его.

Фильтр поиска Angular2: PLUNKR (ОБНОВЛЕНИЕ: плункер больше не работает)

Конец мая
источник
3

Труба будет лучшим подходом. но ниже один также будет работать.

<div *ng-for="#item of itemsList">
  <ng-container *ng-if="conditon(item)">
    // my code
  </ng-container>
</div>
Хардик Патель
источник
это может сломать определенные вещи. например, внутри mat-form-field
pcnate
2

Это мой код:

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>
Палди Герго
источник
1

Другой подход, который мне нравится использовать для фильтров, специфичных для приложения, заключается в использовании настраиваемого свойства только для чтения в вашем компоненте, которое позволяет более аккуратно инкапсулировать логику фильтрации, чем использование настраиваемого канала (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>

Я нахожу для специализированных фильтров, которые являются специфическими для приложения, это работает лучше, чем конвейер, поскольку сохраняет логику, связанную с фильтром с компонентом.

Трубы лучше работают для фильтров многоразового использования.

Рик Страл
источник
1
Не вызовет ли этот метод непрерывные грязные проверки вместо использования метода valueChanged?
Леон Пеллетье
1

Я создал следующую трубу для получения нужных элементов из списка.

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>
Санчит Тандон
источник
1

В идеале вы должны создать Angualr 2 трубы для этого. Но вы можете сделать этот трюк.

<ng-container *ngFor="item in itemsList">
    <div*ngIf="conditon(item)">{{item}}</div>
</ng-container>
sh977218
источник
1

Основываясь на предложенном выше очень элегантном решении обратного вызова, можно немного обобщить его, разрешив передавать дополнительные параметры фильтра. Затем мы имеем:

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>
Blablalux
источник
0

Вот пример, который я создал некоторое время назад и написал в блоге, который включает в себя рабочий план. Он предоставляет канал фильтрации, который может фильтровать любой список объектов. Вы просто указываете свойство и значение {key: value} в своей спецификации 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/

long2know
источник
0

Первый шаг, который вы создаете Filter, используя @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>
Петр Р
источник
0

Это ваш массив

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);
        }
Гаджендер Сингх
источник
0

После некоторого поиска в Google, я столкнулся ng2-search-filter. Он возьмет ваш объект и применит условие поиска ко всем свойствам объекта, которые ищут совпадение.

alindber
источник
0

Я нашел кое-что для создания фильтра, передающего Объект, тогда я могу использовать его как мульти-фильтр: Пример с несколькими фильтрами

я сделал это решение для красоты:

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">...
Ричард Агирре
источник