Angular Pipes
Pipes format values in templates with |
(e.g., date, currency, percent).
Pipes Essentials
- What: Pipes format values in templates using
|
(e.g., date, currency, percent). - Async: The
async
pipe subscribes to Observables and renders the latest value, unsubscribing automatically. - Presentation-only: Pipes change how a value is displayed, not the underlying data.
- Pure by default: Pure pipes run when input references change; avoid in-place mutation of arrays/objects.
{{ title | uppercase }}
{{ price | currency:'USD' }}
{{ today | date:'short' }}
Notes:
- Related: See Templates for displaying values, Data Binding for interpolation and inputs, and HTTP for streams of data with
async
. - Import
CommonModule
for built-in pipes in standalone components. - For custom pipes, use
@Pipe({ standalone: true })
and add the pipe to the componentimports
.
Basic Pipes
- Format strings, numbers, dates, and more with built-in pipes.
- Many accept options (e.g.,
currency:'USD'
,date:'short'
). - Keep business logic in components/services; pipes are for presentation.
{{ title | uppercase }}
{{ price | currency:'USD' }}
{{ today | date:'mediumDate' }}
{{ percent | percent:'1.0-2' }}
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
template: `
<h3>Pipes</h3>
<p>{{ title | uppercase }}</p>
<p>{{ price | currency:'USD' }}</p>
<p>{{ today | date:'mediumDate' }}</p>
<p>{{ percent | percent:'1.0-2' }}</p>
`
})
export class App {
title = 'Angular';
price = 1234.5;
today = new Date();
percent = 0.3495;
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
uppercase
/currency
/date
/percent
: Built-in pipes format strings, numbers, and dates.- Options: Many pipes accept parameters (e.g.,
currency:'USD'
,date:'mediumDate'
,percent:'1.0-2'
). - Pure by default: Pure pipes recompute when the input reference changes; avoid in-place mutation.
Notes:
- Performance: Avoid heavy computation in pipes; Use precomputing in components/services.
- Pure pipes and mutations: Pure pipes run when the input reference changes. If you mutate arrays/objects in place, the pipe will not re-run—create a new reference instead.
Async Pipe
- Render the latest value from an Observable.
- Unsubscribes automatically when the view is destroyed.
- Use
value$ | async as value
to subscribe once and reuse.
<ng-container *ngIf="users$ | async as users; else loading">
<li *ngFor="let u of users">{{ u.name }}</li>
</ng-container>
<ng-template #loading>Loading...</ng-template>
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { interval, of } from 'rxjs';
import { map, delay } from 'rxjs/operators';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
template: `
<h3>Async Pipe</h3>
<p>Time: {{ time$ | async | date:'mediumTime' }}</p>
<h4>Users (delayed)</h4>
<ng-container *ngIf="users$ | async as users; else loading">
<ul>
<li *ngFor="let u of users">{{ u.name }}</li>
</ul>
</ng-container>
<ng-template #loading>Loading...</ng-template>
`
})
export class App {
time$ = interval(1000).pipe(map(() => new Date()));
users$ = of([{ name: 'Alice' }, { name: 'Bob' }, { name: 'Carol' }]).pipe(delay(1200));
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
time$ | async
: Subscribes in the template and renders the latest time value.*ngIf="users$ | async as users; else loading"
: Creates a single subscription, assigns tousers
, and shows a fallback template while loading.- Auto-unsubscribe: The
async
pipe cleans up when the view is destroyed.
Notes:
- Single subscription: Avoid using
| async
multiple times on the same Observable in the same template area; use| async as value
once and reusevalue
. - Loading placeholders: Combine with
*ngIf
and anelse
template for a friendly loading state. - Lists: When iterating streamed arrays, add
trackBy
to*ngFor
for stable identity and better performance.
Custom Pipe
- Build small, reusable formatters with
@Pipe
. - Mark as
standalone
and import into components. - Pure by default; avoid impure pipes unless necessary.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'titlecase2', standalone: true })
export class TitleCase2Pipe implements PipeTransform {
transform(v: string): string { /* ...format... */ return v; }
}
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, Pipe, PipeTransform } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Pipe({ name: 'titlecase2', standalone: true })
export class TitleCase2Pipe implements PipeTransform {
transform(value: string): string {
if (!value) return '';
return value
.split(/\s+/)
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
.join(' ');
}
}
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, FormsModule, TitleCase2Pipe],
template: `
<h3>Custom Pipe</h3>
<label>
Text: <input [(ngModel)]="text" placeholder="type here" />
</label>
<p>Original: {{ text }}</p>
<p>TitleCase2: {{ text | titlecase2 }}</p>
`
})
export class App {
text = 'hello angular pipes';
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
@Pipe({ standalone: true })
: Declares a reusable, importable pipe.transform(value: string)
: Implements the formatting logic and returns a string.- Usage: In the template, apply with
{{ text | titlecase2 }}
.
Notes:
- Impure pipes: They run on every change detection and can hurt performance. Use pure pipes; only mark impure when strictly necessary.
- No side effects: Keep pipes deterministic and free of side effects (no logging, no service calls).
- Null safety: Handle
null
/undefined
inputs gracefully to avoid template errors.