Sitemap

How to avoid multiple async pipes in Angular

3 min readNov 1, 2019

TL;DR: make sure that you don’t expand the same observable more than once in your async pipes. Use ngIf trick for observable expansion and combine it with grouping the observable into a single object.

So many async pipes…

If you are not new to Angular, you know that async pipes are generally better than the subscribe blocks. The following code is more or less identical.

With subscribe block:

With theasync pipe:

I think it’s clear why we love async pipes. They unsubscribe on component destroy making it unnecessary to clean up manually. The code becomes smaller and much easier to read.

Sometimes, however, you need to show the asynchronous data in multiple places of the same component. For example, you check the user privileges and display different buttons.

This doesn’t look nice. We don’t want to use async pipe twice because it will create two subscriptions instead of one. Multiple subscriptions can be harmful. It’s not uncommon that double subscription would lead our frontend to perform two identical http-calls instead of one, or dispatch identical actions to your state management engine, or do heavy calculations more than once.

Normally we would create a wrapper and use the *ngIf trick. We wrap the html tags into a div, span or ng-container, where we expand the observable:

If we have non-boolean data in the observable, we would use the as syntax to create a local variable:

and if we have multiple observables… well, we will have to create multiple wrappers because we can’t use multiple ngIf’s on the same element:

There are two problems with that approach. First, is that it looks too artificial to create a container just to expand observable. Secondly, *ngIf directive will not show its content unless the observable is expanded. For example, if the observable depends on a http call that takes 3 seconds to complete, you will not see any content of the *ngIf block for these 3 seconds.

Imagine now, that we are the data for isCompetenceCatalogEditor$ come from one microservice, the offers$ come from another one and the $products are provided by a third one. In this case you will see the content only when all observables produce a value, and if one the services fail or takes too much time to respond, the screen will remain to be blank.

Sometimes it’s not what we need. Maybe you don’t like that the screen remains blank just because the observables are not expanded. Maybe in your case it’s important to prevent flickering and to show at least something while the user waits for the http-calls to finish.

Solution — *ngIf trick, modified

What I propose instead is to group the observables into an object and deal with this object in the *ngIf directive:

In this case, the value of *ngIf will always be thruthy — even when none of the observables emit values. That means that all code inside the container will always be displayed. When the observables start emitting values, we will see their results.

Analysis

  • This approach is good when you have a lot of observables and you need to use their values in a lot of places in the template.
  • This approach prevents flickering and increases the perceived page load speed. It’s not acceptable when the requirements are to see the whole page with all data.
  • One alternative to this approach would be to use multiple async pipes and manually make sure that it uses shared observables. I find it a bit more nervous than just having one async pipe.
  • Another alternative would be to combine multiple observables into one using combineLatest, merge, or forkJoin. In my opinion, that adds too much code.

--

--

Yury Katkov
Yury Katkov

Responses (9)