Hey guys,
In the previous post we have learned about the Data Binding in Angular 2.0
In this post we’re going to take a detailed look at pipes in Angular 2. Pipes in Angular 2 are basically what filters were in Angular JS 1. As they allow you to transform values from one format to another within your views. For example, supposing you receive some data from a service that has time in raw format like 2016-06-02T12:58:32+00:00 and you want to display it in a much readable format like May 16, 2016. You can do that by using pipes in Angular 2.
Using pipes in your views is as simple as using the pipe | sign with the name of the pipe inside your binding(s). For example, if we use Angular 2’s built in date pipe to transform our date object to a much readable format (the example I mentioned above), we would write the following code:
import { Component } from '@angular/core' @Component({ selector: 'date-view', template: `<p>Date: {{ created | date }}</p>` }) export class DateViewComponent { created = new Date(2016, 6, 2); // June 02, 2016 }
Supposing you want the date pipe to format the date in a particular manner? Angular 2 allows you to do that also, by just passing parameters to the pipe. Consider the example below, where we are passing a format string as a parameter to the date pipe after the | pipe symbol:
<p>Created on: {{ created | date:"MMMM/dd/yy HH:mm:ss" }} </p>
You can also chain or put together multiple pipes to achieve the effect you want. For example, if you want to show the date in all caps, you can use the UppercasePipe in conjunction with the DatePipe (as shown in the snippet below).
<p>Created on: {{ created | date:"MMMM/dd/yy HH:mm:ss" | uppercase }} </p>
Angular 2 comes with a number of built-in pipes (like the date pipe we used above) that offer basic functionality. Some of the other built-in pipes you can use include the JsonPipe, which gives you nicely formatted Json, the UpperCasePipe(which we also used in the example above), the LowerCasePipe and the CurrencyPipe, to name a few. To see the complete list of built-in pipes that Angular 2 offers, head on over toAngular Docs.
As you can see, these built-in pipes offer a variety of basic functions but unlike Angular JS, there are no built-in filter or Order By pipes. We’ll look at why these are not included in Angular 2 in another post.
In this tutorial, I’ll show you how to create a custom pipe. Click here to run Live example for the demo of this section.
Lets start off by creating a simple filter pipe. For instance, a simple to-do app that has two components. The first component in this app is the TodoListComponent that holds an array of to-dos. The second one, the TodoItemComponent is the child component that renders an individual to-do item that we pass to it from the parent component.
Here is the code for this pipe.
import { Pipe, PipeTransform } from '@angular/core'; import { Todo } from './todo'; @Pipe({name: 'todoFilter'}) export class TodoFilterPipe implements PipeTransform { transform(todos, filter) { if(filter) return todos.filter(todo => todo.content.startsWith(filter)); return todos; } }
As you can see from the snippet above, the TodoFilterPipe is decorated with the Pipe metadata that defines the name that we use in the template. You’ll also notice that our pipe class implements the PipeTransform interface that uses thetransform(…) method. This is the method that Angular 2 will call with parameters whenever our pipe is used.
In the above example, the TodoFilterPipe takes a list of to-dos and filters it based on the filter parameter that you provide. Here is how this pipe is used inside the TodoListComponent template:
import { Component } from '@angular/core'; import { Todo } from './todo'; import { TodoItem } from './todo-item.component'; import { TodoFilterPipe } from './todo-filter.pipe'; @Component({ selector: 'todo-list', directives: [TodoItem], pipes: [TodoFilterPipe], template: ` <input type="text" placeHolder="filter todos" [(ngModel)]= "filterText"> <p>List of Todos: </p> <ul> <todo-item *ngFor="let todo of todos | todoFilter: filterText" [todo]="todo"></todo-item> </ul> <small>Total items: {{todos.length}}</small> ` }) export class TodoList { public filterText:string; public todos: Array<Todo>; }
The key thing to note in the above example is how we’ve used the TodoFilterPipe with *ngFor, and how the filterText is passed as a parameter using the : symbol (as depicted in the snippet below).
<todo-item *ngFor="let todo of todos | todoFilter: filterText" [todo]="todo"></todo-item>
Angular 2 invokes the transform method of the pipe, using the value of the binding as the first argument. Notice how the parameter name is not included in the expression here, like we used in the built-in date pipe above.
If you run the live example of this tutorial, you will see that the pipe now filters our to-dos based on the filter text that we type.
Let create another pipe that hides the completed to-do item based on user input. Here is the TodoDonePipe for that:
import { Pipe, PipeTransform } from '@angular/core'; import { Todo } from './todo'; @Pipe({name: 'todoDone'}) export class TodoDonePipe implements PipeTransform { transform(todos: Array<Todo>, hideDone: boolean) { if(hideDone) return todos.filter(todo => todo.done == false); else return todos; } }
The pipe we’ve used above filters out to-do’s that are marked ‘done’ if the hideDone check is true. Let’s chain this pipe with our filter pipe (as shown below) and see what happens.
<todo-item *ngFor="let todo of todos | todoFilter: filterText | todoDone: hideCompleted" [todo]="todo"></todo-item>
With these 2 pipes chained together, you can now toggle completed tasks with a check box binded to the hideCompleted checkbox. If you run the live Example and add a new to-do item in that, with the Hide Completed checkbox marked, you will see that it does not show up in the list unless you check it again. Why is that? Let’s look at this issue in the section below.
By default pipes are pure. Angular 2 optimizes pure pipes by checking if they need to be invoked again if the inputs change. In the above example the items are being pushed to the to-do array i.e. todos.push(). This does not change the reference to the array. We just are mutating the array object by adding more items to it.
In the above example since the reference does not change, Angular 2 does not invoke our pipe. Hence the view goes out of sync with our model. This is the way change detection works in Angular 2.
You might be wondering why this optimization is necessary. That’s because change detection can be an expensive operation and can even crash your app if it’s not done right, which is why it needs to be optimized. Change detection in Angular 2 is a whole topic in itself that we will cover in later posts.
As opposed to pure pipes, impure pipes are executed whenever Angular 2 fires the change detection cycle regardless of any change in its input value. Since it is executed every time, we need to make sure our pipes don’t do expensive operations which can hog memory and ruin the app’s user experience. How do you create an impure pipe? By simply turning off the pure flag in the pipe’s metadata (as shown below).
import { Pipe, PipeTransform } from '@angular/core'; import { Todo } from './todo'; @Pipe({ name: 'todoDone' pure: false }) export class TodoDonePipe implements PipeTransform { transform(todos: Array<Todo>, hideDone: boolean) { if(hideDone) return todos.filter(todo => todo.done == false); else return todos; } }
In our examples above, the TodoFilterPipe takes two parameters. The todos and the filter string based on which we filter the todos.content on. An alternate method for passing more than one parameter to the pipe is to just pass them as an array (as shown in the snippet below).
<todo-item *ngFor="let todo of todos | todoFilter: [param1, param2]" [todo]="todo"></todo-item>
This isn’t a good approach though, since by doing so you would lose named parameters in the transform()method and would need to define it as:
transform(todos:Array <Todo>, params[]: Array<any>)
Since your parameters are defined as TypeScript rest parameters, you can use a better approach to pass these parameter to your pipe, by modifying youtransform() method to:
transform(todos:Array <Todo>, param1, param2){}
Here’s the best way to pass multiple parameters to your pipe:
<todo-item *ngFor="let todo of todos | todoFilter: param1: param2" [todo]="todo"></todo-item>
USA408 365 4638
1301 Shoreway Road, Suite 160,
Belmont, CA 94002
Whether you are a large enterprise looking to augment your teams with experts resources or an SME looking to scale your business or a startup looking to build something.
We are your digital growth partner.
Tel:
+1 408 365 4638
Support:
+1 (408) 512 1812
COMMENTS ()
Tweet