Angular Async Pipe - Here's How To Use It Today (Step-by-step)


Who else wants to use the async pipe in Angular?

And why the async pipe feels like you're ridding in a big elevator.

The Angular framework seems to have a crush on reactive programming.

With a special affection for RxJS.

That's why the RxJS library is a dependency in every Angular project that I've ever seen.

And it's the reactive programming that newbies complain about - it does a splendid job of twisting their brain. That is, until they get the hang of reactive programming and then they start to enjoy it. Well... maybe not everyone but most of us get the hang of RxJS after a while... and end up with a crush on it just like the Angular framework.

But RxJS isn't a silver bullet.

Reactive programming introduces a set of its own challenges.

laugh

Have you ever seen something like this in one of your Angular components?

$observable.subscribe((result) => {
    // do something with the result here
});

And then a little while later in the same file something like this?

$observable.unsubscribe();

Anytime we subscribe to an Observable we need to make sure that we unsubscribe as well. Plus, that subscribe stuff with the return result is rather hairy and makes my code unclean.

What if there was a superior way to handle Observables and Promises?

better%20way

That's where the async pipe in Angular emerges and toots its horn.

What is the Angular async pipe?

Angular's async pipe is a pipe that subscribes to an Observable or Promise for you and returns the last value that was emitted.

That means we don't have to do any more of this. 👇

$observable.subscribe((result) => {
    // do something with the result here
});

Every time a new value is emitted the async pipe will automatically mark your component to be checked for changes.

And best of all, when the component is destroyed the async pipe will automatically unsubscribe for you. No need to do this manually.

no%20need

Why should we use Angular's async pipe?

Any Angular developer worth his salt has gotta admit that the async pipe is quite terrific.

You should be using it because...

  • If you subscribe() to an Observable or Promise you'll need to unsubscribe() at the end of your component's life cycle to avoid memory leaks.
  • Change detection works splendidly with the async pipe.
  • We don't have to unsubscribe manually because the AsyncPipe handles this for us.

Not to mention that it makes our code more readable and clean.

And using it makes you feel like ridding in a big elevator. 😏

Angular async pipe simple example

So how do we use Angular's async pipe?

Here's a beginner's example.

import { Component, OnInit } from '@angular/core';
import { Observable, timer } from 'rxjs';

@Component({
  selector: 'app-timer',
  template: `
    <p>Timer: {{ $timer | async }}</p>
  `,
  styleUrls: ['./timer.component.css']
})
export class TimerComponent implements OnInit {

  $timer: Observable<Number>;

  constructor() { }

  ngOnInit(): void {
    this.$timer = timer(1000, 1000);
  }

}

We begin by declaring a new timer in our TimerComponent class. This timer is configured to start in one second and then fire every second after that.

Then, in our template, we print the timer results by using the async pipe.

Angular AsyncPipe with Observables

Imagine we have an Observable that emits a stream of people.

How do we use the AsyncPipe to handle this observable?

Here's how it's done.

import { Component, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs';

@Component({
  selector: 'app-timer',
  template: `
    <p>People: {{ $observable | async }}</p>
  `,
  styleUrls: ['./timer.component.css']
})
export class PeopleComponent implements OnInit {

  $observable: Observable<any>;

  constructor() { }

  ngOnInit(): void {
    this.$observable = of(["Jim", "Dick", "Harry"]);
  }

}

Angular async pipe with Promises

But what about Promises?

Here's a simple demo that resolves immediately with a list of names - similar to our Observable example.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-people',
  template: `
    <p>{{ $observable | async }}</p>
  `,
  styleUrls: ['./people.component.css']
})
export class PeopleComponent implements OnInit {

  $observable: Promise<any>;

  constructor() { }

  ngOnInit(): void {
    this.$observable = Promise.resolve(["Jim", "Dick", "Harry"]);
  }

}

What about a Promise with a timer?

The async pipe example below will display foo after a 3-second wait.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-timer',
  template: `
    <p *ngIf="($observable | async) as time">Current Timer Value: {{ time }}</p>
  `,
  styleUrls: ['./timer.component.css']
})
export class TimerComponent implements OnInit {

  $observable: Promise<any>;

  constructor() { }

  ngOnInit(): void {
    this.$observable = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('foo');
      }, 3000);
    });
  }
}

How to use the async pipe with *ngIf?

import { Component, OnInit } from '@angular/core';
import { Observable, timer } from 'rxjs';

@Component({
  selector: 'app-timer',
  template: `
    <p *ngIf="($observable | async) as time">Current Timer Value: {{ time }}</p>
  `,
  styleUrls: ['./timer.component.css']
})
export class TimerComponent implements OnInit {

  $observable: Observable<Number>;

  constructor() { }

  ngOnInit(): void {
    this.$observable = timer(5000, 1000);
  }
}

How to use the async pip with *ngFor

import { Component, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs';

@Component({
  selector: 'app-people',
  template: `
    <ul>
      <li *ngFor="let user of $users | async">{{user}}</li>
    </ul>
  `,
  styleUrls: ['./people.component.css']
})
export class PeopleComponent implements OnInit {

  $users: Observable<string[]>;

  constructor() { }

  ngOnInit(): void {
    this.$users = of(['Alice', 'Jane', 'Dan', 'Mary'])
  }
}

How to use the async pipe with HttpClient

We'll use a simple Angular component for this demo.

Our component will inject the Angular HTTP service (even though this is considered diddly) and call the boredapi.com website to retrieve an idea in case we ever get bored.

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

class Activity {
  activity: string;
  type: string;
  link: string;
}

@Component({
  selector: 'app-activity',
  template: `
    <h1>Activity Summary</h1>
    <div *ngIf="($activity | async) as activity; else loading">
       <p>{{ activity.activity }}</p>
       <p>{{ activity.type }}</p>
    </div>
    <ng-template #loading>
      <p>Loading...</p>
    </ng-template>
  `,
  styleUrls: ['./activity.component.css']
})

export class ActivityComponent implements OnInit {

  $activity: Observable<Activity>;

  constructor(private httpClient: HttpClient) { }

  ngOnInit(): void {
    this.loadActivity();
  }

  loadActivity() {
    this.$activity = this.httpClient.get<Activity>("https://www.boredapi.com/api/activity");
  }
}

So what do you think?

The Angular async pipe is quite the handy little pipe.

And I think it's reasonable to say that a well-designed Angular application should never need to subscribe to an Observable. The async pipe can take care of subscribing and unsubscribing while we worry about more important stuff like the structure of our Angular application and so forth.

What do you think of the AsyncPipe?

signature

Angular Consultant

P.S. - If you're a skimmer like me than here's what this article is all about.

  • Angular has a crush on RxJS that gives Angular devs some challenges. Especially newbies.
  • What is the Angular async pipe and why should you use it.
  • How to use the Angular async pipe with Observables, Promises, the ngIf and the ngFor, as well as Angular's HTTP client.
  • Why the async pipe makes you feel like ridding in a big elevator.