DEV Community

Cover image for How to simplify nested async subscriptions in Angular template
Dany Paredes
Dany Paredes

Posted on • Edited on

How to simplify nested async subscriptions in Angular template

One way to subscribe to observable in Angular is async pipe into the HTML template. It is easy but when you have several subscription use ng-container with *ngIf is a common solution, like :

<ng-container *ngIf="userAuth$ | async as user">
    <span column-1 class="licence-name">
        {{user.role}}
    </span>
    <ng-container *ngIf="domains$ | async as domains">
      <ul *ngFor="let domain in domains">
        <li>{{domain}}</li>
    </ng-container>
<ng-container *ngIf="ads$ | async as ads">
       <div *ngFor="let ad in ads">
           {{ad.name}}
       <div>
</ng-container>
<ng-container 
</ng-container>
Enter fullscreen mode Exit fullscreen mode

use Object :)

The ng-contanier generate too noise into the DOM, but we can simplify using object into a single *ngIf and grouping each subscription into it object like:

<ng-container *ngIf="{
    user: userAuth$ | async,
    domains: domains$ | async
} as state ">
    <span column-1 class="licence-name">
        {{state.user.role}}
    </span>
      <ul *ngFor="let domain in state.domains">
        <li>{{domain}}</li>
      </ul>
</ng-container>
Enter fullscreen mode Exit fullscreen mode

Hopefully, that will give you a bit to help you avoid nested *ngIf for observable subscription.

If you enjoyed this post, share it.

Photo by John Barkiple on Unsplash

Top comments (3)

Collapse
 
mandel profile image
Julien Fractal IT

I strongly advise against using this kind of pattern.

First, you are breaking your ngIf clause, as *ngIf="userAuth$ | async as user" will be false if user is undefined, whereas *ngIf="{ ... }" will always be true.
Thus your refactoring actually changes the behavior.

Secondly, in my opinion, this is just a hacky way to avoid an angular bug with nested async pipes.
Those observables should be combined in the component using rxjs operators like switchMap or combineLatest. This way the logic of updating the component will be perfectly clear.

Collapse
 
danywalls profile image
Dany Paredes • Edited

Hi @mandel !
You're absolutely right, combineLatest is a great alternative! In fact, I talk about it in other article and how it can save you from the hassle of using multiple async pipes. 🎉

  playerData$ = combineLatest([this.player$, this.stats$]).pipe(
    map(([info, stats]) => ({ info, stats }))
  );
Enter fullscreen mode Exit fullscreen mode
<h1>Nba</h1>
  <div *ngIf="playerData$ | async as playerData">
    <h2>{{ playerData.info.first_name }} {{ playerData.info.last_name }}</h2>
    <h3>Stats</h3>
    <ul>
      <li *ngFor="let stat of playerData.stats.data">
        Points: {{ stat.pts }} Rebounds: {{ stat.reb }} Steals: {{ stat.stl }}
      </li>
    </ul>
  </div>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
bezael profile image
Bezael Pérez

Congrats 👏