370

Apparently, Angular 2 will use pipes instead of filters as in Angular1 in conjunction with ng-for to filter results, although the implementation still seems to be vague, with no clear documentation.

Namely what I'm trying to achieve could be viewed from the following perspective

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

How to implement so using pipes?

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
Khaled
  • 8,255
  • 11
  • 35
  • 56
  • 11
    Note that a breaking change is introduced in beta 17 for ngFor regarding the hash symbol. The correct way is: `
    – Memet Olsen May 09 '16 at 08:23
  • 12
    @MemetOlsen comment from Gunter below: "`*ngFor` and `*ngIf` on the same element are not supported. You need to change to the explicit form for one of them" – Nate Anderson Jan 24 '17 at 00:36
  • 1
    Even tho it's what the OP asks for, it's recommanded to NOT USE PIPE for filtering or ordering in Angular2+. Prefer having a class property with the filtered values : https://angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe – ylerjen Dec 21 '18 at 14:33

25 Answers25

512

Basically, you write a pipe which you can then use in the *ngFor directive.

In your component:

filterargs = {title: 'hello'};
items = [{title: 'hello world'}, {title: 'hello kitty'}, {title: 'foo bar'}];

In your template, you can pass string, number or object to your pipe to use to filter on:

<li *ngFor="let item of items | myfilter:filterargs">

In your pipe:

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);
    }
}

Remember to register your pipe in app.module.ts; you no longer need to register the pipes in your @Component

import { MyFilterPipe } from './shared/pipes/my-filter.pipe';

@NgModule({
    imports: [
        ..
    ],
    declarations: [
        MyFilterPipe,
    ],
    providers: [
        ..
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

Here's a Plunker which demos the use of a custom filter pipe and the built-in slice pipe to limit results.

Please note (as several commentators have pointed out) that there is a reason why there are no built-in filter pipes in Angular.

Michael
  • 8,362
  • 6
  • 61
  • 88
phuc77
  • 6,717
  • 2
  • 15
  • 11
  • Thanks for the answer! Is anyone else getting the error message _ExpressionChangedAfterItHasBeenCheckedException_? [Angular IO docs](https://angular.io/docs/ts/latest/api/core/ExpressionChangedAfterItHasBeenCheckedException-class.html) say it gets thrown when application changes upset the _top-down_ data flow.. Any thoughts? – SimonHawesome Jan 18 '16 at 17:06
  • I found the solution! As outlined in [this post](http://stackoverflow.com/questions/34456430/ngfor-doesnt-update-data-with-pipe-in-angular2), the _change detection strategy_ needs to be set to _always_. – SimonHawesome Jan 18 '16 at 18:30
  • 7
    Thanx, this work as intended, but sometimes it's better to check if the items array is defined and not null, because Ng2 may try to apply filter while the "items" still undefined. – timmz Mar 17 '16 at 16:50
  • 1
    In addition, I needed to add the filter class to the @Component declaration. Like So: @Component({... pipes: [MyFilterPipe ] – Stephen Jul 11 '16 at 18:01
  • 1
    I think it also needs this line ìf (!items) return items;` in case the array is empty. – Boštjan Pišler Dec 21 '16 at 16:02
  • 1
    `@Injectable()` shouldn't be necessary if you're just using the pipe in a template. yalls remember to declare the pipe in your module though. – Alexander Taylor Feb 10 '17 at 16:58
  • isnt there a generic way fo doing this? – user230910 Apr 27 '17 at 12:55
  • 1
    There is no Generic way of doing this built-in for a reason! https://angular.io/guide/pipes#no-filter-pipe – austin_ce Jul 12 '17 at 18:19
  • 3
    Angular says using a Pipe has performing issues, so recommends to make filtering on the component – Sebastián Rojas Jul 17 '17 at 19:31
  • @phuc77 i am getting this error **Property 'title' does not exist on type 'Object'** on pipe page when i am doing sorting from textbox. – Abhay Singh Sep 14 '17 at 05:57
  • @AbhaySingh Not quite sure I understand what you mean? Sorting from textbox? I don't have any examples of sorting in my answer or plnkr, just filtering. If you have an issue with something you've made, I suggest you create a new question. – phuc77 Sep 14 '17 at 08:34
  • @phuc77 i saw in plunkr i can search title/id/year etc. from their respective textbox. i want get help on that – Abhay Singh Sep 14 '17 at 09:22
  • @AbhaySingh Try adding typing to your object instead of using "Object". In my plnkr, I have defined Book as a type. – phuc77 Sep 14 '17 at 09:40
  • 5
    I'd like to suggest to wrap `*ngFor` parameters in parenthesis, just to avoid any confusion and make it "change-proof": `
  • `
  • – Tomas May 25 '18 at 10:36
  • 1
    You may use a pure pipe if the elements of the list are never modified. This has performance benefits (the filter does not have to be re-applied on every change detection). – cambunctious Sep 11 '19 at 17:42
  • What about filtring the data and checking for non null values by suing Rxjs filter() operator before binding data to HTML template , it will be more performance than suing pipes or filter with *ngFor , always avoid logic inside the template or the component – Rebai Ahmed Jul 13 '21 at 23:12