Async Pipe to the Rescue?

OK. But come on, I just want to subscribe to some observables when the component is created and then unsubscribe from all when it’s destroyed.

Why do I have to write a single line of code for that? It seems to be the most common case. Why do I have to handle it on my own? Can’t the framework help me with that?

Well, it can. There’s the async pipe in Angular.

You can use the async pipe in your component template. You pass to it an observable and then it subscribes to it, it returns values from it and finally it unsubscribes from it when the component is destroyed. Everything we need. At least, at first glance…

Imagine we have a lastComment$ observable returning the last comment object. And, imagine we want to print the comment body in the template. We can do it easily like so:

<h1>Last comment</h1>

<div>{{ (lastComment$ | async).body }}</div>

So far so good. But what if we want to add the comment subject also?

<h1>Last comment</h1>

<h2>{{ (lastComment$ | async).subject }}</h2>

<div>{{ (lastComment$ | async).body }}</div>

Uhm… not so cool anymore. We have two subscriptions instead of one. Each use of the async pipe creates a separate subscription (even if you use the same observable).

Imagine that under the lastComment$ observable, you have hidden an HTTP request to an API or some complex data processing. With two separate subscriptions, you will do those operations twice instead of once. It does not sound OK, does it? So, what can we do?

Well, we can help ourselves by making use of the *ngIf directive. Let’s wrap everything in a div element. Let’s add *ngIf to it and subscribe to lastComment$ inside of it.

<div *ngIf="lastComment$ | async">

...

</div>

Whenever there will be a comment emitted, the div content will be shown. Whenever there is no comment at all, the entire div will be hidden. Perfect.

We can keep the async pipe result under the variable and use it later in the element revealed by the *ngIf . It’s something you’ve probably seen many times. Let’s call our variable comment .

<div *ngIf="(lastComment$ | async) as comment">

<h1>Last comment</h1>

<h2>{{ comment.subject }}</h2>

<div>{{ comment.body }}</div>

</div>

OK, so it looks like we nailed it, right?

Yeah… But…

Now imagine your component template is a bit bigger. And you display the comment data in two or more places, separated by some other content. Let’s say we want to print the comment author’s name, then a blog post, and then all the comment details, like so:

Uhm… Again. As you can see, we have multiple subscriptions.

Can we deal with them? Will our trick — the wrapping div — will it work again? Let’s see:

And, no…It doesn’t work anymore — at least not always.

Please notice: when there is no lastComment at all, the async pipe returns null . And then the *ngIf directive hides the entire div with all its content, together with the innocent blogPost element that we want to keep visible.

So, is the async pipe useless in such scenarios? Are we forced to manage subscriptions manually? Or do we need to live with multiple subscriptions in templates?

Luckily, you can still use the async pipe! You just need to know how.