DEV Community

Atilla Baspinar
Atilla Baspinar

Posted on

Angular Pipes

A pipe transforms a value in the template using the | operator. Pipes do not mutate the original value — they return a new transformed one.

<p>{{ username | uppercase }}</p>
<p>{{ dueDate | date:'mediumDate' }}</p>
Enter fullscreen mode Exit fullscreen mode

1. Built-in pipes

All built-in pipes come from @angular/common. In standalone components, import each pipe individually rather than the entire CommonModule.

import { DatePipe, UpperCasePipe, CurrencyPipe } from '@angular/common';

@Component({
  imports: [DatePipe, UpperCasePipe, CurrencyPipe],
  ...
})
Enter fullscreen mode Exit fullscreen mode
Pipe Example Output
uppercase `'hello' \ uppercase`
lowercase `'HELLO' \ lowercase`
titlecase `'hello world' \ titlecase`
date `date \ date:'mediumDate'`
number `3.14159 \ number:'1.0-2'`
currency `9.9 \ currency:'USD'`
percent `0.85 \ percent`
slice `'Angular' \ slice:0:3`
json `obj \ json`
keyvalue `obj \ keyvalue`
async `obs$ \ async`

Pipe parameters

Pass parameters after the pipe name with :. Chain multiple parameters with more colons.

<!-- date format string -->
<p>{{ dueDate | date:'yyyy-MM-dd' }}</p>

<!-- number: minIntegers.minFractionDigits-maxFractionDigits -->
<p>{{ 3.14159 | number:'1.0-2' }}</p>              <!-- 3.14 -->

<!-- currency: currencyCode, display, digitsInfo -->
<p>{{ 9.9 | currency:'EUR':'symbol':'1.2-2' }}</p> <!-- €9.90 -->
Enter fullscreen mode Exit fullscreen mode

Chaining pipes

Apply multiple pipes left to right:

<p>{{ username | slice:0:10 | uppercase }}</p>
Enter fullscreen mode Exit fullscreen mode

async pipe

AsyncPipe subscribes to an Observable or Promise and returns its latest emitted value. It automatically unsubscribes when the component is destroyed.

@if (users$ | async; as users) {
  @for (user of users; track user.id) {
    <li>{{ user.name }}</li>
  }
}
Enter fullscreen mode Exit fullscreen mode

In modern Angular, toSignal() is often a cleaner alternative — it converts the Observable to a signal so you can read the value with users() in the template without needing the async pipe.


2. Custom pipes

Create a pipe with the @Pipe decorator and implement the PipeTransform interface. The transform() method receives the input value as its first argument, followed by any additional parameters you declare.

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

@Pipe({
  name: 'truncate',
  standalone: true,
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, limit: number = 20, ellipsis: string = ''): string {
    return value.length > limit ? value.slice(0, limit) + ellipsis : value;
  }
}
Enter fullscreen mode Exit fullscreen mode

Import it in the component and use it in the template. Extra arguments map to the additional parameters in transform(), in order:

@Component({
  imports: [TruncatePipe],
  ...
})
Enter fullscreen mode Exit fullscreen mode
<p>{{ description | truncate }}</p>           <!-- limit=20, ellipsis='…' -->
<p>{{ description | truncate:50 }}</p>        <!-- limit=50, ellipsis='…' -->
<p>{{ description | truncate:50:'...' }}</p>  <!-- limit=50, ellipsis='...' -->
Enter fullscreen mode Exit fullscreen mode

3. Pure vs impure pipes

By default every pipe is pure — Angular only calls transform() when the input value or parameters change by reference. If you mutate a property inside an object or push an item into an array without replacing the reference, a pure pipe will not re-run, because the object or array reference itself has not changed.

Set pure: false to make a pipe impure — Angular then calls transform() on every change detection cycle, regardless of whether the reference changed.

@Pipe({
  name: 'filterActive',
  standalone: true,
  pure: false,
})
export class FilterActivePipe implements PipeTransform {
  transform(items: Item[]): Item[] {
    return items.filter(item => item.active);
  }
}
Enter fullscreen mode Exit fullscreen mode

With pure: true (the default), pushing a new item into items would not trigger the pipe. With pure: false, it runs on every cycle and picks up the change — but also runs constantly, which can hurt performance on large lists.

Prefer component logic over impure pipes. Filter or sort in the component class and expose the result as a property or computed signal. This gives you control over when the work runs and avoids the performance cost of running on every change detection cycle.

Top comments (0)