Angular pipes are a powerful feature that allow developers to transform data in their Angular applications, making it easier to display, format, and manipulate data in the view layer. However, like any other feature in Angular, pipes should be used wisely to ensure optimal performance and maintainability of your application. In this article, we will explore the best practices for using Angular pipes efficiently for data transformation.
Angular provides several built-in pipes for common use cases, such as formatting dates, numbers, and strings. Additionally, you can create your own custom pipes to suit specific needs. In this article, we’ll explore the basics of using built-in and custom pipes in Angular, along with some examples.
Table of Contents
Angular Pipes: Best Practices for Efficient Data Transformation
Angular provides several types of built-in pipes that you can use out of the box. Here are the different types of pipes in Angular:
Pure Pipes
Pure pipes are the most common type of pipes in Angular. They are called “pure” because they always return the same output for the same input, and they do not have any side effects. Pure pipes are automatically re-evaluated only when their input values change, which makes them efficient in terms of performance. Examples of pure pipes are DatePipe
, UpperCasePipe
, LowerCasePipe
, CurrencyPipe
, and JsonPipe
.
Date Pipe
This pipe is used for formatting dates. It takes a date value as input and formats it according to the specified format.
1 2 3 4 | <!-- Example usage of DatePipe --> <p>{{ currentDate | date:'dd/MM/yyyy' }}</p> |
Here are more options available in
DatePipe
.
UpperCasePipe and LowerCasePipe
These pipes are used for converting strings to uppercase and lowercase, respectively.
1 2 3 4 5 | <!-- Example usage of UpperCasePipe and LowerCasePipe --> <p>{{ message | uppercase }}</p> <p>{{ message | lowercase }}</p> |
CurrencyPipe
This pipe is used for formatting numbers as currencies. It takes a number value as input and formats it as a currency according to the specified locale.
1 2 3 4 | <!-- Example usage of CurrencyPipe --> <p>{{ price | currency:'USD':'symbol' }}</p> |
JsonPipe
This pipe is used for converting objects or arrays to JSON strings.
1 2 3 | <!-- Example usage of JsonPipe --> <pre>{{ data | json }} |
Impure Pipes
Impure pipes, as the name suggests, are not “pure” because they can have side effects and may return different output for the same input. Impure pipes are re-evaluated on every change detection cycle, which can impact the performance of your application. Therefore, it is recommended to use pure pipes whenever possible and avoid impure pipes unless necessary. An example of an impure pipe is AsyncPipe
, which is used for subscribing to observables in templates.
AsyncPipe
The AsyncPipe
is a built-in pipe in Angular that provides a convenient way to handle asynchronous data streams, such as promises or observables, in the view layer. Let’s take a look at an example of how to use the AsyncPipe
in an Angular component.
Imagine we have a simple Angular application that displays a list of users fetched from an API. We have a UserService
that fetches the users from the API and returns an observable of User[]
. Here’s how we can use the AsyncPipe
to display the list of users in the component’s template:
1. Create the User model:
1 2 3 4 5 6 7 8 | // user.model.ts export interface User { id: number; name: string; email: string; } |
2. Create the UserService to fetch users from the API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // user.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { User } from './user.model'; @Injectable() export class UserService { private apiUrl = 'https://jsonplaceholder.typicode.com/users'; constructor(private http: HttpClient) {} getUsers(): Observable<User[]> { return this.http.get<User[]>(this.apiUrl); } } |
3. Update the component to use the AsyncPipe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // user.component.ts import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { User } from './user.model'; import { UserService } from './user.service'; @Component({ selector: 'app-user', template: ` <p>Users</p> <ul> <li *ngFor="let user of users$ | async">{{ user.name }}</li> </ul> `, }) export class UserComponent { users$: Observable<User[]>; constructor(private userService: UserService) { this.users$ = this.userService.getUsers(); } } |
In the component’s template, we use the users$
observable returned from the UserService
and pass it through the AsyncPipe
using the async
pipe operator. The AsyncPipe
subscribes to the users$
observable and automatically handles the asynchronous data stream, including subscription management, error handling, and automatic data binding to the template. This way, whenever the users$
observable emits a new value, the template will be automatically updated with the latest data.
4. Result in browser
The result looks like this-

Custom Pipes
Custom pipes are created by you, the developer, to suit your specific requirements. Custom pipes can be either pure or impure, depending on how you implement them. As mentioned earlier, custom pipes are created as classes with the Pipe
decorator and implementing the PipeTransform
interface. Custom pipes allow you to extend the functionality of Angular’s built-in pipes or create entirely new ways to transform data in your application.
FilterUsers Custom Pipe
Let’s create a custom pipe in Angular that filters a list of users based on a search term. Here’s an example:
1. Create the FilterUsersPipe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // filter-users.pipe.ts import { Pipe, PipeTransform } from '@angular/core'; import { User } from './user.model'; @Pipe({ name: 'filterUsers' }) export class FilterUsersPipe implements PipeTransform { transform(users: User[] | null, searchTerm: string): User[] | null { if (!users || !searchTerm || searchTerm.trim() === '') { return users; // Return the original list if no search term is provided } // Filter users based on the search term searchTerm = searchTerm.toLowerCase(); return users.filter( (user) => user.name.toLowerCase().includes(searchTerm) || user.email.toLowerCase().includes(searchTerm) ); } } |
2. Import FilterUsersPipe in AppModule:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { HttpClientModule } from '@angular/common/http'; import { FilterUsersPipe } from './filter-users.pipe'; @NgModule({ declarations: [AppComponent, FilterUsersPipe], imports: [FormsModule, BrowserModule, AppRoutingModule, HttpClientModule], bootstrap: [AppComponent], }) export class AppModule {} |
3. Update the component to use the FilterUsersPipe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // user.component.ts import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { User } from './user.model'; import { UserService } from './user.service'; @Component({ selector: 'app-user', template: ` <p>Users</p> <input [(ngModel)]="searchTerm" placeholder="Search by name or email"> <ul> <li *ngFor="let user of users$ | async | filterUsers : searchTerm">{{ user.name }}</li> </ul> `, }) export class UserComponent { users$: Observable<User[]>; searchTerm!: string; constructor(private userService: UserService) { this.users$ = this.appService.getUsers(); } } |
In the component’s template, we use the filterUsers
custom pipe to filter the list of users based on the searchTerm
input value. The searchTerm
is bound to an input field using [(ngModel)]
for two-way data binding. Whenever the input value changes, the filterUsers
pipe is automatically triggered to filter the list of users based on the new search term.
4. Output
Check the output in browser-

Parameterized Pipes
Parameterized pipes are a type of custom pipe that allows you to pass additional parameters to the transform
method. These parameters can be used to customize the behavior of the pipe. To pass parameters to a pipe, you can include them after the pipe name in the template using a colon (“:”) followed by the parameter value. For example: {{ data | customPipe:param1:param2 }}
. You can then access these parameters in your custom pipe’s transform
method as additional arguments.
Conclusion
Pipes are a powerful feature in Angular that allows you to transform data in your application’s view layer. Angular provides various built-in pipes for common use cases, and you can also create your own custom pipes to suit specific requirements. It’s important to understand the different types of pipes in Angular, such as pure pipes, impure pipes, custom pipes, and parameterized pipes, and use them appropriately in your application to ensure optimal performance and maintainability.