Using Async Pipe in Angular

The async pipe accesses an Observable directly from inside the HTML template. The Observable lifecycle is managed by Angular, no need for us to manage subscribe and unsubscribe.

Published: 6/6/2024

A simple oneliner to display the value, no manual subscription management, or risk of memory leaks.

The service provides basic message state management to be displayed by the component.

  • The first paragraph displays [object Object]. Not very useful.
  • The second paragraph displays the value of the message. Exactly what we wanted.
import {Component, inject, Injectable} from '@angular/core';
import {AsyncPipe} from '@angular/common';
import {BehaviorSubject, Observable} from 'rxjs';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [AsyncPipe],
  template: `
    <p>{{message$}}</p>
    <p>{{message$ | async}}</p>
    <button (click)="changeMessage()">Change message</button>
  `,
})
export class AppComponent {
  appService = inject(AppService);

  public message$ = this.appService.message$;

  changeMessage() {
    this.appService.message$ = "Good bye!";
  }
}

@Injectable({
  providedIn: 'root'
})
export class AppService {

  private _message$ = new BehaviorSubject<string>("Hello world!");
  public get message$(): Observable<string> {
    return this._message$.asObservable();
  }
  public set message$(value: string) {
    this._message$.next(value);
  }
}

Request and display the post, presenting a loading message while waiting on the response.

A simple oneliner, no observable subscriptions.

The service provides a mocked http request to get the post.

  • A Loading... message is displayed while the post is being requested
  • The loaded post can be freely referenced inside the ng-container
import {Component, inject, Injectable} from '@angular/core';
import {AsyncPipe, NgIf} from '@angular/common';
import {map, Observable, timer} from 'rxjs';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [AsyncPipe, NgIf],
  template: `
    <ng-container *ngIf="post$ | async as p; else loading">
      <h2>{{ p.title }}</h2>
      <p>{{ p.excerpt }}</p>
    </ng-container>

    <ng-template #loading>Loading...</ng-template>
  `,
})
export class AppComponent {
  appService = inject(AppService);

  public post$ = this.appService.getPost$();
}

@Injectable({
  providedIn: 'root'
})
export class AppService {
  public getPost$(): Observable<Post> {
    return timer(500).pipe(
      map(() => ({title: "Title", excerpt: "Excerpt"}))
    );
  }
}

interface Post {
  title: string;
  excerpt: string;
}

Request an display a list of posts, utilising the loading message pattern from the previous example.

A simple oneliner, no observable subscriptions.

The service provides a mocked http request to get the posts.

  • A Loading... message is displayed while the posts are being requested
  • Use trackBy to update only list elements that changed, read about trackBy
import {Component, inject, Injectable} from '@angular/core';
import {AsyncPipe, NgForOf, NgIf} from '@angular/common';
import {map, Observable, timer} from 'rxjs';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [AsyncPipe, NgForOf, NgIf],
  template: `
    <ng-container *ngIf="posts$ | async as ps; else loading">
      <ng-container *ngFor="let post of ps; trackBy: trackByFn">
        <h1>Hello {{ post.title }}</h1>
        <p>Hello {{ post.excerpt }}</p>
      </ng-container>
    </ng-container>

    <ng-template #loading>Loading...</ng-template>
  `,
})
export class AppComponent {
  appService = inject(AppService);

  public posts$ = this.appService.getPosts$();

  trackByFn(index: number, post: Post): string {
    return post.id; // or another unique identifier on the item
  }
}

@Injectable({
  providedIn: 'root'
})
export class AppService {
  public getPosts$(): Observable<Post[]> {
    return timer(500).pipe(
      map(() => ([
        {id: "1", title: "Title 1", excerpt: "Excerpt 1"},
        {id: "2", title: "Title 2", excerpt: "Excerpt 2"}
      ]))
    );
  }
}

interface Post {
  id: string;
  title: string;
  excerpt: string;
}

Continue reading: Reactive Error Handling in Angular