ng-filter in Angular2 (pipes)

Filters are a great way to manipulate and filter data in AngularJS, in Angular they got a new name: Pipes. Worry not! They're just as easy to implement and a ton of fun.

Creating the project

We're going to use the Angular2 cli again. Install it with (sudo) npm i angular-cli -g or if you have installed it already but you're not sure if you have the latest version you can run (sudo) npm update angular-cli -g.

This time we're going to work with a list of famous people and create a filter pipe to filter by age.

The Angular 1.x way

I created our desired application in Angular 1.5.

In this example we create a filter called ageFilter. The filter has a default "value" parameter as first function argument (mandatory for every filter) and a second parameter called minAge that specifies the minimum age to filter.

angular.filter('ageFilter', function() {  
    return function(value, minAge) {
      return value.filter(function (person) {
          return person.age >= +minAge;
      });
    }
  });

As you can see we return a new array (the result of value.filter(...) fn) with all people that have an age that is greater than or equal to minAge.

If you're new to JavaScript you might be confused by the 2 .filter functions you see. The first one is a custom Angular filter and the second one is a default JavaScript array method.

The Angular2 way

The first thing we do in Angular2 is create our new Pipe. It's going to look like this:

import {Pipe} from 'angular2/core';

// Tell Angular2 we're creating a Pipe with TypeScript decorators
@Pipe({
  name: 'AgePipe'
})
export class AgePipe {

  // Transform is the new "return function(value, args)" in Angular 1.x
  transform(value, args?) {
    // ES6 array destructuring
    let [minAge] = args;
    return value.filter(person => {
      return person.age >= +minAge;
    });
  }

}

That's our Pipe. Looks quite easy right?

Next up the the actual component that lists our famous people! Let's begin with the component that displays our data.

import {Component} from 'angular2/core';  
import {AgePipe} from './pipes/age-pipe/age-pipe';


@Component({
  selector: 'playground-app',
  templateUrl: 'app/playground.html',
  // tell our component it uses our AgePipe
  pipes: [AgePipe]
})
export class PlaygroundApp {  
  sliderValue:number = 20;

  people:Array<any> = [{
      name: 'Justin Bieber',
      age: 21
    }, {
      name: 'Miley Cyrus',
      age: 23
    }, {
      name: 'John Legend',
      age: 37
    }, {
      name: 'Betty White',
      age: 94
    }, {
      name: 'Roger Waters',
      age: 72
    }, {
      name: 'Larry Page',
      age: 42
    }];
}

There's really not much to say about this component, it's important that you import the Pipe and add it to the component decorator though! All we need is our HTML and we're good to go!

<div>  
  <p>
  0
    <input type="range" min="0" max="100" 
          [value]="sliderValue" 
          (input)="sliderValue = $event.target.value" />
  100
  </p>
  <span>{{ sliderValue }}</span>
    <ul>
      <li *ngFor="#person of people | AgePipe:sliderValue">
        {{ person.name }} ({{ person.age }})
      </li>
    </ul>
</div>  

We could even simplify our <input type="range"/> by removing the [value] and (input) handlers and replace them with just [(ngModel)]="sliderValue", like this:

<input type="range" min="0" max="100" [(ngModel)]="sliderValue" />  

Array destructuring and filters

In our Pipe we have a little array destructuring magic:

 transform(value, args?) {
    let [minAge] = args;
    ...

This is extremely powerful when you have multiple arguments. Let's say you also want to set a maxAge. All we would have to do is add another <input type="range" [value]="anotherSliderValue" (input)=".." /> change the HTML to:

  <li *ngFor="#person of people | AgePipe:sliderValue:anotherSliderValue">
      {{ person.name }} ({{ person.age }})
  </li>

And finally refactor our Pipe to:

  transform(input, args?) {
    let [minAge, maxAge] = args;
    return input.filter(person => {
      return person.age >= +minAge && person.age <= +maxAge;
    });
  }

Done

This was the fourth post into my "Angular 1.x built in directives in Angular 2.0" series. I like to keep the posts small and simple with a little bit of useful information in each of them.

If you liked it don't hesitate to message me on Twitter @Jilles.