Angular Built-in Pipes: Date, Async, KeyValue and Using Pipes in Code

Angular Tutorial: Beginner to Advanced · 6. Reusable components · 5 min read · Updated

The pipes lesson introduced date, currency and custom pipes. Here are the three pipes people ask about most, plus how to call a pipe from TypeScript code.

What you will learn

  • Date formats and time zones
  • The async pipe and why it prevents leaks
  • keyvalue, slice and json
  • Using a pipe in a component or service

Date pipe

// src/app/pipes2/date-demo.ts
import { Component } from '@angular/core';
import { DatePipe } from '@angular/common';

@Component({
  selector: 'app-date-demo',
  imports: [DatePipe],
  template: `
    <ul>
      <li>Short: {{ when | date: 'short' }}</li>
      <li>Long date: {{ when | date: 'longDate' }}</li>
      <li>Custom: {{ when | date: 'dd/MM/yyyy HH:mm' }}</li>
      <li>Weekday and month: {{ when | date: 'EEEE, MMMM d' }}</li>
      <li>UTC: {{ when | date: 'yyyy-MM-dd HH:mm' : 'UTC' }}</li>
    </ul>
  `,
})
export class DateDemo {
  protected readonly when = new Date(Date.UTC(2026, 9, 10, 14, 30));
}

Useful format letters: yyyy year, MM month number, MMMM month name, dd day, EEEE weekday name, HH 24-hour, hh 12-hour, mm minutes, a AM or PM. The second argument after a colon is a time zone such as 'UTC'. A pipe accepts a Date, a number (timestamp) or an ISO string.

Async pipe

Data from HTTP and many other sources comes as Observables. The async pipe subscribes for you, shows the latest value and unsubscribes automatically when the component is destroyed, so it cannot leak:

// src/app/pipes2/async-demo.ts
import { Component } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { interval, map, of, delay } from 'rxjs';

@Component({
  selector: 'app-async-demo',
  imports: [AsyncPipe],
  template: `
    <p>Seconds: {{ seconds$ | async }}</p>

    @if (user$ | async; as user) {
      <p>User: {{ user.name }}</p>
    } @else {
      <p>Loading user...</p>
    }
  `,
})
export class AsyncDemo {
  protected readonly seconds$ = interval(1000);
  protected readonly user$ = of({ name: 'Asha' }).pipe(delay(800));
}

Two tips: end Observable property names with $ as a naming habit, and use @if (x$ | async; as value) so you subscribe once. Writing x$ | async several times in a template creates several subscriptions. With signals you may prefer toSignal(), but the async pipe is still everywhere in existing code and is perfectly good.

KeyValue pipe

@for loops over arrays. To loop over an object, convert it with keyvalue:

// src/app/pipes2/keyvalue-demo.ts
import { Component } from '@angular/core';
import { KeyValuePipe } from '@angular/common';

@Component({
  selector: 'app-keyvalue-demo',
  imports: [KeyValuePipe],
  template: `
    <dl>
      @for (entry of scores | keyvalue; track entry.key) {
        <dt>{{ entry.key }}</dt>
        <dd>{{ entry.value }}</dd>
      }
    </dl>
  `,
})
export class KeyvalueDemo {
  protected readonly scores: Record<string, number> = { Maths: 92, Science: 88, English: 79 };
}

Each entry has a key and a value. By default entries are sorted by key. Pass a compare function to change that: scores | keyvalue: byValue.

Slice and json

<p>First three: {{ items | slice: 0 : 3 }}</p>
<p>Text start: {{ 'Angular pipes' | slice: 0 : 7 }}</p>
<pre>{{ user | json }}</pre>

slice takes part of an array or string. json prints an object, which is great for debugging and the one pipe you should remove before shipping.

Using pipes in components and services

Pipes are normal classes, so you can call them from TypeScript. Provide them (or create one) and call transform:

// src/app/pipes2/price-formatter.ts
import { CurrencyPipe, DatePipe } from '@angular/common';
import { Injectable, inject } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class Formatter {
  private readonly currency = new CurrencyPipe('en-US');
  private readonly date = inject(DatePipe, { optional: true }) ?? new DatePipe('en-US');

  price(value: number): string {
    return this.currency.transform(value, 'USD') ?? '';
  }

  day(value: Date): string {
    return this.date.transform(value, 'fullDate') ?? '';
  }
}
// src/app/pipes2/pipes-page.ts
import { Component, inject } from '@angular/core';
import { Formatter } from './price-formatter';
import { DateDemo } from './date-demo';
import { AsyncDemo } from './async-demo';
import { KeyvalueDemo } from './keyvalue-demo';

@Component({
  selector: 'app-pipes-page',
  imports: [DateDemo, AsyncDemo, KeyvalueDemo],
  template: `
    <h2>Pipes</h2>
    <p>From code: {{ formatter.price(1234.5) }} on {{ formatter.day(today) }}</p>
    <app-date-demo />
    <app-async-demo />
    <app-keyvalue-demo />
  `,
})
export class PipesPage {
  protected readonly formatter = inject(Formatter);
  protected readonly today = new Date(Date.UTC(2026, 9, 10));
}
// src/app/app.ts
import { Component } from '@angular/core';
import { PipesPage } from './pipes2/pipes-page';

@Component({
  imports: [PipesPage],
  selector: 'app-root',
  templateUrl: './app.html',
  styleUrl: './app.css',
})
export class App {}
<!-- src/app/app.html -->
<main>
  <app-pipes-page />
</main>

Creating new CurrencyPipe('en-US') is the simplest way to use a pipe in a service, because you pass the locale yourself. When the same formatting is needed in many places, wrap it in a small service like Formatter.

Pure and impure pipes

By default a pipe is pure: Angular re-runs it only when its input value or reference changes. That is fast, but it means a pure pipe will not notice if you mutate an array in place. Make the change with a new array ([...items, x]) and the pipe updates. Avoid impure pipes (pure: false), because they run on every change detection.

Try it yourself

  1. Show a "posted" time with date: 'shortTime', then change the time zone.
  2. Use keyvalue to display a settings object as a definition list.
  3. Replace the seconds$ | async with toSignal(this.seconds$) and compare.

Quick check

  1. Why is the async pipe safe against memory leaks?
  2. Which pipe lets @for loop over an object?
  3. How do you format a value inside a service?

Continue with the component lifecycle, and see the pipes reference.

Frequently asked questions

Should I use the async pipe or toSignal?

Both are fine. toSignal fits code that uses signals everywhere, and the async pipe is simple for one value in a template.

Why does my date pipe show the wrong day?

Dates are shown in the browser's local time zone by default. Pass a time zone, such as 'UTC', as the second argument if you need a fixed one.