Async Pipe in Angular: Understanding and Implementing

Learn about the Async Pipe in Angular and how to implement it in your project. This powerful feature allows you to easily handle asynchronous data and keep your templates in sync with your component's data.

Async pipe in Angular
Handle Asynchronous Data Like a Pro

The async pipe is one of the most used pipes in Angular, as it allows to work of asynchronous data in the component's template.

It makes reactiveness a simple solution for almost all requirements.

If you're struggling with how to handle asynchronous data in Angular, this article is for you.

In this beginner-friendly article, we'll take a step-by-step look at:

What is the Angular Async Pipe?

Async pipe is a built-in feature in Angular that allows you easily bind asynchronous data to your templates.

It works by subscribing to an observable or a promise and returns the latest value that has been emitted.

This means that you can use the async pipe to display real-time data, without the need for explicit subscriptions or manual event handling.

One of the biggest async pipe benefits is automatically unsubscribing. Once the component utilizing the async pipe is destroyed, the pipe automatically unsubscribes, preventing any memory leaks.

This mechanism allows you to handle asynchronous data in your templates effortlessly without having to worry about subscriptions or manual event handling.

Here's why you should use the Async pipe in Angular:

  1. Automatically unsubscribes when the component is destroyed.
  2. Avoid boilerplate code.
  3. Helps avoid memory leaks.
  4. Works with Observables and Promises.
  5. Makes the component's logic much shorter.

However, with all its advantages, it has disadvantages as well:

  1. Marks the component and all its ancestors as dirty - see the alternative provided in this article.
  2. Doesn't trigger the component's change detection.

Let's dive more in-depth into these advantages and disadvantages, and see how to use the Async pipe in Angular.

How to use Angular Async Pipe

Using the async pipe is easy. All you need to do is apply the pipe to the variable you want to bind the asynchronous data to.

But first, make sure you imported CommonModule from @angular/common in app.module.ts or if your component is standalone, CommonModule is inside your component's imports list.

For example, if you have an observable that returns the current time, use the async pipe to bind this data to your template with the following code snippet:

<p>The current time is {{ time$ | async }}</p>

In this example, the time$ variable is observable that emits the current time. By using the async pipe, you can bind the latest value emitted by this observable to your template.

Now, let's deep dive and take a look at the more advanced example of how to use the async pipe in Angular.

Let's say we want to show the Bitcoin price in real-time.

We'll send a request once in five seconds and use the async pipe to update the price in the template.

We won't use .subscribe() in our template, we'll let the async pipe handle subscribing and unsubscribing for us.

In general, subscribing inside the component should be avoided, when is possible, as it requires more work to unsubscribe, if you don't do it correctly, you risk a possible memory leak 😈.

Here's our code:

import { Component } from '@angular/core';
import { interval } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { switchMap, map } from 'rxjs/operators';

const BTC_PRICE_HOST = 'https://api.coindesk.com/v1/bpi/currentprice/BTC.json'

@Component({
  selector: 'app-root',
  template: `
    <p>Bitcoin Price: {{ bitcoinPrice$ | async | currency }}</p>
  `
})
export class AppComponent {
  bitcoinPrice$ = interval(5000).pipe(
      switchMap(() => this.http.get<{ last: number }>(BTC_PRICE_HOST)),
      map(data => data.last)
  );

  constructor(readonly http: HttpClient) { }
}

First, we define bitcoinPrice$ variable as Observable by assigning the RxJS interval function.

We send 5000 to interval function as milliseconds, which means the interval will emit sequential numbers every 5 seconds.

Now is time to transform the data, we put the switchMap operator and return an HTTP Request from coindesk.com API, later, by providing another operator map, we transform the data from the request and return just what we need - data.last  - this contains the current price of Bitcoin.

Async pipe subscribes to bitcoinPrice$ and every 5 seconds and updates the template with the current Bitcoin price.

We'll see how to catch errors later in this article, let's now remain here in our example and see what the async pipe is doing with bitcoinPrice$.

How Does Async Pipe trigger Change Detection?

It doesn't trigger the Change Detection, the async pipe marks the component and all its ancestors as dirty.

The change detection is only activated when the zone.js microtask queue is emptied and the ApplicationRef.tick function is executed, which renders all components marked as dirty.

When using the async pipe in a zone-less environment, we must manually trigger change detection each time an observable produces a new value.

If you don't understand what's a zone-less environment, that's okay, you probably don't need to.

ngrxPush Pipe: A good alternative to Angular Async Pipe

The ngrxPush pipe is an alternative to the async pipe that can be used without making any changes to your code.

It can handle change detection automatically, allowing it to work in both modes, zone-less or zone-full. This means you can use it without having to worry about how to handle new updates.

To use the ngrxPush pipe first we must install @ngrx/component library:

npm i @ngrx/component

After, import PushModule to app.module.ts or in case of a standalone component, add PushPipe straight to the imports list:

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [PushPipe],
    template: `
    	<p>Bitcoin Price: {{ bitcoinPrice$ | ngrxPush | currency }}</p>
    `
})

See the official documentation on how to use ngrxPipe pipe, you'll also find a good alternative of ngIf directive.

An alternative that should be avoided

An alternative without using the async pipe is to use .subscribe inside the component and use a variable instead of an observable.

Let's see an example:

import { Component } from '@angular/core';
import { interval } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { switchMap, map, takeUntil } from 'rxjs/operators';

const BTC_PRICE_HOST = 'https://api.coindesk.com/v1/bpi/currentprice/BTC.json'

@Component({
  selector: 'app-root',
  template: `
    <p>Bitcoin Price: {{ bitcoinPrice | currency }}</p>
  `
})
export class AppComponent implements OnInit, OnDestroy {
  readonly onDestroy$ = new Subject();
  bitcoinPrice$ = interval(5000).pipe(
      switchMap(() => this.http.get<{ last: number }>(BTC_PRICE_HOST)),
      map(data => data.last)
  );

  bitcoinPrice: number;

  ngOnDestroy(): void {
      this.onDestroy$.next('')
      this.onDestroy$.complete()
  }

  ngOnInit(): void {
      bitcoinPrice$.pipe(takeUntil(this.onDestroy$)).subscribe(price => {
          this.bitcoinPrice = price;
      })
  }

  constructor(readonly http: HttpClient) { }
}

Besides that our code grew to the sky, we also must make sure we did everything right, because if we'd followed some of Angular's best practices and would change changeDetection to ChangeDetectionStrategy.OnPush, we'd need to handle change detection on our own, which means even more code to be added.

takeUntil operator in this example is very important, it unsubscribes from the observable when onDestroy$ emit something - this is the key to avoiding memory leaks when using .subscribe().

Read more about what a Subject is, or other types of Subjects in Angular.

Handle Errors with the Async Pipe

Errors are a part of any application, and the async pipe can help you handle them.

If the observable or promise you are subscribing to emits an error, the async pipe will catch it and pass it to the template.

Let's see how to catch the errors in our Bitcoin example:

@Component({
  selector: 'app-root',
  template: `
    <ng-container *ngIf="bitcoinPrice$ | async as price; else loadingOrError">
    	<p>Bitcoin Price: {{ price | currency }}</p>
    </ng-container>
    <ng-template #loadingOrError>
    	<p *ngIf="priceError">{{ priceError }}</p>
    	<p *ngIf="!priceError">Price is loading ...</p>
    </ng-template>
  `
})
export class AppComponent {
    priceError: string;
    bitcoinPrice$ = interval(5000).pipe(
        switchMap(() => this.http.get<{ last: number }>(BTC_PRICE_HOST)),
        map(data => data.last),
        catchError((e: HttpErrorResponse) => {
            this.priceError = e.message;
            return throwError(() => e.message)
        })
    );

    constructor(readonly http: HttpClient) { }
}

We wrapped our paragraph into an ng-cointainer and used the ngIf directive. If bitcoinPrice$ returns the price, and no error occurs, ngIf assign the coming price to the template price variable - as price. In this case, we can show it safely.

The most interesting part of this example is when an error occurs! Let's start with the component changes.

As you can see, I've added a new variable priceError, and marked it as string, this is where we'll put our HttpErrorResponse message.

Another change I've made is the catchError operator, it catches the error whenever one occurs - this is what we need. We get the error message and assign it to the priceError variable and show it later in our template.

The catchError operator must return an Observable with an error or message, so we throwError with the error message.

Back to the template, once ngIf get an error, it associates it as false and embeds loadingOrError template reference to the ng-container view container - else loadingOrError.

Inside the loadingOrError template we have two ngIf's directive, if priceError has a value, show the message, if not, show the loading message.

Simple!

Even though this example looks nice, catching errors inside components is not a good practice. Find here the best strategies to handle the errors in Angular.

Conclusion

The async pipe is a valuable tool for any Angular developer, especially beginners.

It allows you to easily handle asynchronous data in your templates and eliminates the need for explicit subscriptions or manual event handling.

Using it is not so complicated, as we've seen in our examples, simple as adding it to the template, also catching errors is easy, even though I recommend using HttpInterceptors for such cases.

You should keep in mind the async pipe in Angular doesn't trigger change detection, it does mark the component as dirty as well as all its ancestors, if you don't understand what this means, don't bother yourself, it's for those who can code with their eyes closed 😎.

By following the examples in this article, you should now have a good understanding of how the async pipe works and how you can use it in your Angular projects.