Angular Async Pipe & Reactive Code

Subscribe to an Observable or Promise in the template and get the latest value it has emitted

Angular Pipe built on a photo by Roma Kaiuk on Unsplash

We can use Angular async pipe to access an Observable or a promise from the template.

The angular documentation describes the async pipe very well: “The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted.“.

I will just expand a bit here:

  • The async pipe subscribes to an Observable or Promise when the component is initialized.
  • It returns each value emitted by the Promise or Observable.
  • When a new value is emitted, the async pipe marks the component to be checked for changes. It runs change detection to update the UI accordingly.
  • When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks.

The last point is especially exciting because we don’t need to unsubscribe manually!

Let’s have a look at the following angular application.

Class component

The class component declares the numbers variable. The variable is of type array of numbers and it is initialized to [1, 3, 5, 7, 9].

import { Component } from '@angular/core';...
export class AppComponent{
numbers: number[] = [1, 3, 5, 7, 9];
}

The template uses numbers to display a paragraph for each element in the numbers array. Each paragraph displays an item from the array.

At this point, we are using *ngIf="numbers" in the template. This is redundant but it will come useful later. Furthermore, it is a straightforward way to handle conditional situations.

<div *ngIf="numbers">  <p *ngFor="let n of numbers">{{ n }}</p></div>

Now, we will make this code reactive.

First, we import OnInit to initialize the new observable when the component is initialized.

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

...
export class AppComponent implements OnInit{
numbers: number[] = [1, 3, 5, 7, 9];

ngOnInit() {}
}

Second, we change the name of the variable numbers to be numbers$ of type Observable<number[]>. The dollar sign $ is a convention to indicate that the variable is an Observable.

Finally, we assign the Observable created by of to the Observable property numbers$. Briefly speaking, the built-in function ofconverts the arguments to an observable sequence.

Here is the code in app.component.ts:

Template

We move to the template to use the Angular async pipe and get the values ​​returned by the numbers$ Observable.

  1. First, we check if the numbers$ Observable is defined with *ngIf=”numbers$”,
  2. then, we pipe the numbers$ Observable through the async pipe eg ”numbers$ | async”, to access its emitted values. As we said above, the angular async pipe automatically subscribes to the observable and handles unsubscribe.
  3. By using as numbers we assign the emitted value to a variable that we can use in the template. In our case, numbers will be an array of numbers.
  4. Finally, we use the numbers variable in ngFor and we iterate through the item in the array to generate a new paragraph for each item.
<div *ngIf="numbers$ | async as numbers">  <p *ngFor="let n of numbers">{{ n }}</p></div>

Here are some of the main benefits that you get while using the async pipe:

  • no need to subscribe
  • no need to unsubscribe
  • better change detection

Change detection refers to the ability to update the UI when the underlying data changes. Thanks to change detection the UI will always show the most updated data from the component.

The most common change detection strategies are

  • Default. It uses the default checkAlways strategy. Checks every component when any change is detected
  • On push. It checks the component for changes only when @Input properties changes, Events emit, or Observables emit. In so doing, the on-push strategy minimizes change detection cycles by using the CheckOnce strategy.

By binding an Observable in the template we follow the on-push strategy. To enable this change detection strategy, we need to set it in the component decorator as follows:

@Component({
templateUrl: './app.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})

If we get an error, the Observable stream stop and does not emit any more items. Therefore, it is important to catch and handle errors properly.

The RxJS catchError operator is an error-handling operator:

  1. It subscribes to an input stream.
  2. If an error occurs, catchError unsubscribes from the input stream and returns a replacement observable. If no error occurs, the input stream emits items to the output stream.
  3. Then, it creates an output stream depending on the occurrence of errors. It might optionally retrow an error.

As reported in the documentation, catchErrorCatches errors on the observable to be handled by returning a new observable or throwing an error. […] Handles errors from the source observable, and maps them to a new observable..

Therefore, we can extrapolate two basic error handling strategies:

  • catch & replace
  • catch & retrow

Both strategies use the catchError operator.

Catch & Replace

Using the catch & replace strategy, we catch an error and replace it with something that is more suitable to continue:

  • an Observable that emits some alternative data
  • an Observable that emits an empty value
  • the EMPTY rxjs constant. The EMPTY constant defines an observable that emits no items and completes.

Using the catch and replace strategy, catchError replaces the error Observable with a new Observable.

// Catch & Replace StrategycatchError((err) => {    this.errorMessage = err;
return EMPTY;
})

The marble diagram in the rxjs documentation explains this strategy well.

The arrows represent Observables and marbles represent emitted values.

In the following catchError marble diagram, we see that an Observable emits the values a and b before encountering an error represented by the X symbol.

Using a catch & replace strategy, catchErrorcatches the error and emits the values ​​1, 2, 3, and complete, represented by the | symbol.

Marble diagram of the rxjs catchError operator
Marble diagram of the rxjs catchError operator

Thanks to this approach, the final outcome will seem like a single Observable stream that emits a, b, 1, 2, 3, and complete.

Catch & Rethrow

The catch & rethrow strategy returns a replacement Observable by using the rxjs throwError creation operator.

The throwError operator “creates an observable that will create an error instance and push it to the consumer as an error immediately upon subscription. This operator emits no items.

The throwError operator is a creation operator:

  1. It creates and returns a replacement Observable that emits no items. Technically, it returns Observable<never>
  2. It emits an error notification and stops the newly created Observable.

This strategy is generally used for error propagation.

// Catch & Rethrow Strategy

catchError((err) => {

return throwError(err);

})

The documentation literally says “Just errors and does nothing else“.

Marble diagram (?) of the rxjs throwError operator
Marble diagram (?) of the rxjs throwError operator
  • Use Angular async pipe to avoid using subscribe and unsubscribe manually. Furthermore, it is a good way to use an RxJS Declarative Pattern in Angular
  • The Angular async pipe optimizes change detection
  • Take care of error handling with catchErroreither by using catch & replace or catch and retrow

Leave a Comment