← Back to Tutorials Chapter 5

Pipes

What Are Pipes?

Pipes transform data displayed in templates. They take an input value and optionally arguments, apply formatting or transformation logic, and return the output for display. Angular provides many built-in pipes, and you can create custom pipes for domain-specific needs.

Syntax: {{ expression | pipeName:arg1:arg2 }}

Built-in Pipes

DatePipe

Formats date values according to locale rules:

<p>{{ today | date }}</p>
<p>{{ today | date:'medium' }}</p>
<p>{{ today | date:'yyyy-MM-dd' }}</p>
<p>{{ today | date:'fullDate' }}</p>

UpperCasePipe and LowerCasePipe

Transform text to upper or lower case:

<p>{{ 'hello world' | uppercase }}</p>
<p>{{ 'HELLO WORLD' | lowercase }}</p>

CurrencyPipe

Formats a number as currency with locale-aware symbol placement:

<p>{{ 99.99 | currency }}</p>
<p>{{ 99.99 | currency:'EUR' }}</p>
<p>{{ 99.99 | currency:'INR':'symbol':'4.2-2' }}</p>

PercentPipe

Formats a number as a percentage:

<p>{{ 0.25 | percent }}</p>
<p>{{ 0.1234 | percent:'2.1-2' }}</p>

JsonPipe

Converts an object to its JSON representation for debugging:

<pre>{{ user | json }}</pre>

AsyncPipe

Unwraps Observables and Promises directly in templates, automatically subscribing and unsubscribing:

<p>{{ observableData$ | async }}</p>
<ul>
  <li *ngFor="let item of items$ | async">{{ item.name }}</li>
</ul>
The AsyncPipe is a powerful tool for handling asynchronous data without manual subscription management. It automatically unsubscribes when the component is destroyed, preventing memory leaks.

DecimalPipe

Formats numbers with decimal places and grouping separators:

<p>{{ 3.14159 | number:'1.2-2' }}</p>
<p>{{ 1234567.89 | number:'5.2-2' }}</p>

Chaining Pipes

Pipes can be chained to apply multiple transformations. Each pipe operates on the output of the previous pipe:

<p>{{ birthday | date:'fullDate' | uppercase }}</p>
<p>{{ price | currency:'USD' | lowercase }}</p>

Order matters — the leftmost pipe is applied first, and its result is passed to the next pipe.

Custom Pipes

Create a custom pipe using the @Pipe decorator and implementing the PipeTransform interface:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'truncate'
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, maxLength: number = 50, suffix: string = '...'): string {
    if (!value) return '';
    return value.length > maxLength
      ? value.substring(0, maxLength) + suffix
      : value;
  }
}

Usage: {{ longText | truncate:30:'…' }}

Pure vs Impure Pipes

Pipes are pure by default. A pure pipe only re-evaluates when its input value changes by reference (not mutation). An impure pipe re-evaluates on every change detection cycle, which is useful for pipes that track internal state or handle mutable data.

@Pipe({
  name: 'filterArray',
  pure: false  // Impure pipe - re-evaluates on every change detection
})
export class FilterArrayPipe implements PipeTransform {
  transform(items: any[], searchTerm: string): any[] {
    if (!items || !searchTerm) return items;
    return items.filter(item =>
      item.name.toLowerCase().includes(searchTerm.toLowerCase())
    );
  }
}
Use impure pipes sparingly. Because they run on every change detection cycle, they can cause performance issues with large datasets or complex transformations. Consider using memoization or restructuring your data flow instead.
Angular pipe transformation

Summary

You now know how to use built-in Angular pipes (date, uppercase, lowercase, currency, percent, json, async, decimal), chain pipes together, and create custom pipes with the PipeTransform interface. You also understand the distinction between pure and impure pipes and when to use each.

Exercise 5: Create a custom pipe called phoneFormat that transforms a 10-digit string into a formatted phone number: (123) 456-7890. Then create a searchFilter pipe (impure) that filters a list of products by name. Use both pipes in a component that displays a searchable, formatted phonebook.