Write Angular like a pro. Angular Icon

Follow the ultimate Angular roadmap.

Handling Observables with NgIf and the Async Pipe

Now you’ve learned the basics of Angular’s NgIf and Else it’s time to take things up a notch and introduce some Observables. In this article you’ll learn how to use Observables with Angular’s NgIf, using the async pipe and practices.

Our friend NgIf has a not-so-obvious feature that lets us will help us deal with asynchronous operations - via the async pipe takes care of subscribing to Observable streams for us.

There are a few common gotchas when dealing with purely cold Observables that somewhat pull in data (over perhaps, Http). There are also a few tricks we can use to mitigate common async issues, whilst being productive in templates.

Everytime we use the async pipe, we create a subscription. If you’re going to subscribe directly to Observables that initiate data transfer, it’s likely you’ve come across unwanted issues such as duplicate Http requests.

There are, of course, ways around this using the .share() operator in RxJS. But it’s more of a work-around, than a “work-with” me.

So let’s explore how we can handle ngIf alongside the async pipe to alleviate some of these common issues.

ngIf and Async Pipe

Let me illustrate a common scenario inside a container/stateful component, where we’d typically use the async pipe to auto-subscribe and pass just raw data down:

<div>
  <user-profile
    [user]="(user$ | async)?.profile">
  </user-profile>
  <user-messages
    [user]="(user$ | async)?.messages">
  </user-messages>
</div>

This approach has a few flaws, the first and most obvious is being potentially exposed to multiple, unwanted, subscriptions (previously mentioned up top) that initiate requests.

Secondly, we’re having to use the safe navigation operator ? before any property names. I don’t know about you, but I find this irritating - it doesn’t fill me with confidence that what I’m doing is structured correctly. Try avoid it wherever possible. We’ll refactor this component’s template before we finish with some best practices.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

The Angular docs state that using the parentheses around the user$ | async is classed an “inefficiency”. I personally find it extremely helpful at expressing intent when used appropriately.

So, what can we do to clean things up a little?

Using ngIf “as” syntax

This feature isn’t just for Observables (but I hope you’re using them anyway!). Let’s assume we’re using something like NGRX Store to make delivering state easier (which also mitigates things like multiple subscription issues that call upon new data requests).

Instead of waiting for each user$ | async operation to be fully available, which likely requires more conditional checking further down inside the child presentational components, we can adopt a slightly different approach:

<div *ngIf="user$ | async as user">
  <user-profile
    [user]="user.profile">
  </user-profile>
  <user-messages
    [user]="user.messages">
  </user-messages>
</div>

Note the addition of “as user” at the end of the expression.

What this will do is wait until user$ | async has evaluated, and bind the result to the value of user (non-dollar-suffixed).

The prop$ dollar suffix is generally used to indicate something is an Observable source.

From this point, you can treat it like function scope in JavaScript. Once the user property has the resulting variable, you can use it anywhere inside that scope (inside the ngIf, not outside).

This also gives us additional flexibility when displaying load state specific data to a user (loading/loaded):

<div *ngIf="user$ | async as user; else loading">
  <user-profile
    [user]="user.profile">
  </user-profile>
  <user-messages
    [user]="user.messages">
  </user-messages>
</div>

<ng-template #loading>
  Loading stuff...
</ng-template>

Read more on ngIf/else syntax.

My personal choice when adopting this syntax would be to use parentheses to express intent, visually it makes it far easier for me to see what’s going on without actually having to process the template in too much detail:

<div *ngIf="(user$ | async) as user; else loading">
  <user-profile
    [user]="user.profile">
  </user-profile>
  <user-messages
    [user]="user.messages">
  </user-messages>
</div>

<ng-template #loading>
  Loading stuff...
</ng-template>

A small deviation from the intention of this post, but a worthy mention. Ideally, data returned from either a selector or server response would be passed down as a whole - I find the syntax more reliable and extensible when passing props down to child components.

Something like this should suffice:

<div *ngIf="(user$ | async) as user; else loading">
  <user-profile
    [user]="user">
  </user-profile>
  <user-messages
    [user]="user">
  </user-messages>
</div>

<ng-template #loading>
  Loading stuff...
</ng-template>

All I’ve done here is remove .profile and .messages from the user binding. Pass the whole object down and use the pieces you need (in this case). Few reasons, namely type checking, interfaces, unit tests. Try it without and you’ll see your codebase explode into further complexity and lacking of structural types.

This approach of course, doesn’t just work with component bindings, you can use it anywhere. But ideally, async stuff should happen in container components, and presentational components should be simply given the data - to render.

Presentation components shouldn’t (in an ideal world) have to worry about checking if properties coming in through @Input bindings actually exist before rendering. We can be smarter, and adopt better patterns through better tools.

And there’s one more for your toolbelt - the async pipe with ngIf and the “as” syntax. It will store the result in a variable of your naming, and you can pass it wherever you like.

Reference away!

To learn more techniques, best practices and real-world expert knowledge I’d highly recommend checking out my Angular courses - they will guide you through your journey to mastering Angular to the fullest!

Learn Angular the right way.

The most complete guide to learning Angular ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover