Async pipe syntax tricks

Yesterday, we wrote about how to use the async pipe to automatically subscribe and unsubscribe from our observables.

When I teach that topic, people usually have at least one of these two objections:

What if I also need the data from that subscription in my Typescript code?

At first, using the async pipe seems only to give you access to the data in your HTML templates. That isn’t the case, though, because you can still use the tap operator to “spy” on your observable and get the data from there. For instance (complete example here):

this.name$ = nameService.getName().pipe(
    tap(name => this.name = name)
);Code language: TypeScript (typescript)

And then in the HTML template:

<p>Name from async pipe: {{ name$ | async }}</p>Code language: HTML, XML (xml)

What if I need to read multiple properties from the object in that subscription?

Another way to put it is that you don’t want to end up doing something like this:

<p>First name: {{ (user$ | async)?.firstName }}</p>
<p>Last name: {{ (user$ | async)?.lastName }}</p>Code language: HTML, XML (xml)

The above code is pretty hard to read and requires one subscription for each property. This alone can be a disaster, as each subscription might trigger an HTTP request for the same data from the server!

Instead, you can do something like this, which uses only one subscription, stores the result in a local variable, then renders the data when it’s available. This technique works with any structural directive, such as *ngIf or *ngFor:

<div *ngIf="user$ | async as user">
   <p>First name: {{ user.firstName }}</p>
   <p>Last name: {{ user.lastName }}</p>
</div>Code language: HTML, XML (xml)

If changing the DOM structure by adding an element to accommodate that subscription bothers you, then you can use ng-template instead, though the syntax here can be a little bit unsettling, too:

<ng-template [ngIf]="user$ | async" let-user>
   <p>First name: {{ user.firstName }}</p>
   <p>Last name: {{ user.lastName }}</p>
</ng-template>Code language: HTML, XML (xml)

Ok, that’s probably plenty enough for today. Tomorrow, we’ll see how we can do even better than this.

How to avoid memory leaks with RxJs observables?

The easiest way to get in trouble in a big Angular application is to create a memory leak by not unsubscribing from your observables. While there are different techniques to unsubscribe your observables automatically, one is more concise, elegant, and overall the most error-proof.

That technique is to use the async pipe from the Angular framework.

Why is the async pipe such a great tool?

  1. First, it automatically subscribes to the observable, so we no longer need to call .subscribe().
  2. It returns the data from that observable, triggering Angular’s change detection when needed for our component to display the latest data.
  3. Finally, it automatically unsubscribes from the observable when our component is destroyed.

All of that with just 6 characters! (ok, perhaps a bit more if you want to count the whitespace):

<div>{{myObservable | async}}</div>Code language: HTML, XML (xml)

The async pipe works exactly like the code we would write (and thus duplicate over and over again, making our code base larger and thus our code slower) if we didn’t use it.

Check its source code here, and you’ll see that all it does is implement ngOnDestroy to unsubscribe from our observable.

There isn’t any good reason not to use the async pipe, and if you think you have some, stay tuned for our next messages, as we’ll cover some nice tips and tricks around using that pipe.

RxJs combineLatest operator

combineLatest has a name that says it all: The operator combines the latest values from two or more observables into one observable.

This marble diagram is the perfect illustration of that behavior (click on the image to access an interactive version):

There are several possible use cases for that operator. The most common usage is filtering information by combining different sources, as illustrated in this tutorial on dynamic filtering with RxJs and Angular forms, which implements the equivalent of an auto-complete text input that displays new suggestions every time the user types new characters in.

What we get out of combineLatest is an array of all the latest values emitted by the combined observables. For instance, if we subscribe to combineLatest(obs1, obs2, obs3), the data we get is an array that contains [lastValueFromObs1, lastValueFromObs2, lastValueFromObs3]. The order of the data in the array matches the order in which we pass the observables to combineLatest, and that array of values will be emitted again every time one of the observables emits again.

A common gotcha of combineLatest is that the operator waits for all observable sources to emit at least one value before returning something, so it can be helpful to use it alongside the startWith operator, as illustrated in this example from our earlier tutorial.

Note: The operator is going to be renamed to combineLatestWith in RxJs 8+.

RxJs switchMap operator

A common mistake made in Angular applications is to nest observable subscriptions in the following manner:

observable1.subscribe(data => {
   observable2.subscribe(otherData => {
     // Do something with otherData
   });
});Code language: JavaScript (javascript)

The above syntax is not recommended because it is hard to read and can lead to subtle bugs and unexpected side effects. For instance, that syntax makes it difficult to unsubscribe properly from all these observables.

Also, if observable1 emits more than once in a short amount of time, we might want to cancel the previous subscription to observable2 and start a new one based on the new data received from observable1. The above code does not do any of that.

The solution: Use the RxJs switchMap operator like so:

observable1.pipe(data => {
   switchMap(data => observable2)
).subscribe(otherData => {
     // Do something with otherData
});Code language: JavaScript (javascript)

The switchMap operator does all of the following:

  • Automatically cancel and unsubscribe from observable2 if observable1 emits a new value.
  • Automatically unsubscribes from observable2 if we unsubscribe from observable1
  • Makes sure that observable1 and observable2 happen in sequence, one after the other.

The following marble diagram is a good illustration of what switchMap does:

If you’re not using switchMap yet, look in your code for nested calls to .subscribe(), and you can start replacing those with the switchMap operator to prevent memory leaks and bugs and make your code more readable.

Here is a link to an example that uses a timer to make HTTP requests every 30 seconds to get updated data. The timer data is turned into an API call using switchMap on line 37 of the example.