In my previous post about Angular subscription management we concluded that the best solution is to use the async pipe. In this post we’ll see a common problem many people ran into with the async pipe.
An observable intro
Before we dive into the details it’s important to make some observations about observables. Observables are functions that tie together a source (e.g. an HTTP request, an event stream) and an observer (your run-of-the-mill Angular component).
The most important aspect of an observable is its temperature. It can either be cold or hot. A cold observable creates its own source, while a hot observable wraps around one.
Cold observables are creating their sources
Consider the following code:
Notice how the observable is creating and dispatching the XMLHTTPRequest. Every subscriber will get a different request, resulting in multiple calls. We’ll review hot observables and then see how to avoid these issues.
Hot observables are wrapping around an existing source
Hot observables are great in the sense that they can share their results with multiple subscribers (they are multicast). However since they are not owning their source they cannot be lazy. Let’s review the same code fragment with a hot observable:
The hot observable is stored in the subject variable. Note that it wraps around our XMLHTTPRequest, which is completely independent from it. The request will always be sent with or without any subscribers.
Note that this is just for presentational purposes. There are better ways to connect subjects with observables. Also, you’d rarely send raw XMLHTTPRequests in Angular, you’d use the built-in HttpClient instead.
Smart usage of the async pipe
After some theoretical background, let’s see how this affects us and our components.
Look at the below component and template code:
callResult$ is a cold observable and we’re in big trouble. The *ngFor directive will trigger one execution, then the expression with the length below will trigger another.
We can fix this with a hot observable like this:
But this is more code, and we don’t like to write and maintain more code. Let’s try a different approach. A better solution would be this:
Using the async pipe with the async as syntax we can define a template variable. It’s called result in our case. The template then can reuse this variable without triggering the observable multiple times. We made our template code agnostic on the temperature of our observable, yayy!