Angular async pipe in depth
The Angular async pipe is one of the ways to subscribe to Observables and Promises in the Angular framework. In most cases, I would say it is the preferred way of handling Observables and Promises in Angular. When you subscribe to an Observable or Promise with the async pipe you will receive the last emitted value. Note that the async pipe needs to be initialized in the HTML template before the value is emitted by the Observable or Promise, otherwise no value is received by the pipe. So if the async pipe is inside a *ngIf and the Observable emits a value before the ngIf evaluates to true nothing is shown, and the async pipe receives no value until a new value is emitted. Using the Angular async pipe is easy and offers some benefits over some more standard ways of subscribing to Observables and Promises.
Table of content:
Why use the Angular async pipe?
How to use the Angular async pipe?
Why use the Angular async pipe?
In Angular when you subscribe to an Observable or Promis you need to unsubscribe from it when the component is destroyed (or the Observable is not used anymore). If you forget to unsubscribe you get memory leaks resulting in all kinds of trouble for your application. With the Angular async pipe the subscriptions are automatically unsubscribed when the component is destroyed. Also if you reassign a property used with an async pipe with a new observable it unsubscribes automatically from the old one and subscribes to the newly assigned Observable or Promise. This greatly reduces the risk of memory leaks in your Angular application.
Another benefit is that the async pipe will mark the template to be checked for changes when it receives a new value. This is helpful when you have your change detection strategy set to OnPush. With a regular subscription, the HTML template is not marketed to be checked for changes if you use the OnPush strategy, meaning you have to trigger the change detection yourself after your subscription receives a new value. So by using the async pipe in combination with the OnPush strategy you get the best of both worlds, automated updates in your HTML template without having to trigger it yourself and without Angular checking it periodically. Using the combination of the async pipe and the OnPush change detection strategy is a good way to boost the performance of your Angular application.
How to use the Angular async pipe?
To start using the Angular async pipe you create a property in your TypeScript file and assign it with an observable. Now you can use this property inside your HTML template with the async pipe next to it. The async pipe will subscribe to the observable for you and resolve the value inside the HTML template. When the component is destroyed the async pipe will unsubscribe from the observable automatically. For our example, I created a sample service with an RxJs Subject we subscribe to and a simple method to update its value with a timeout that fakes a duration for our Observable.
In the TypeScript file, you can see the property to which we assign the observable. The property name is prefixed with a $, this is a common practice for observables. There is also a function call in the Typescript file to update the Observable value in the service.
Now we can use the $testObservable property in our HTML template together with the async pipe to receive its value and display it in our view like this:
By using the async pipe we have to write less code and the code we write is less prone to memory leaks. If we would do the same without the async pipe our code would look something like this:
As you can see there is more code needed in the TypeScript file when we do not use the async pipe. When the number of subscriptions grows so does the chance you forget to unsubscribe one resulting in a lot of trouble. The boilerplate code also starts to pile up when you are handling multiple subscriptions making your files more bloated, and harder to read and maintain.
But now you might be asking yourself, what happens when I want to perform some logic inside the brackets of the subscription, how would this be handled with an async pipe? For this Rxjs Pipe and Map come to the rescue! We can pipe and map on our observable to add any logic we want before sending the value to the HTML template. As an example I will add some text to the observable value before we send it back to the HTML:
Using the async pipe in multiple ways
You can use the async pipe and the value it yields in multiple ways in your HTML templates. You might for example want to check the value inside a *ngIf or *ngFor, or use the value in multiple places in the HTML template. Let's have a look at multiple scenarios and the ways we can handle this.
Angular async pipe with a template variable
In some cases, you might need the resolved value of the async pipe in multiple places in your HTML template. One way to do this is by using the async pipe multiple times in the template, but this creates multiple subscriptions putting a toll on your performance.
This does work but we can do better for sure, now let's assign a variable name to the async pipe so we can use the value in multiple places without using the async pipe multiple times. One of the most common ways to do this is by using a *ngIf on the parent element with the async pipe inside the *ngIf, that way you can safely use the values in the child elements. Just close the async pipe with a semicolon and create a let variable after it to assign the async value to the variable.
If not all child elements need to wait on the async pipe to resolve a value and there is no clear HTML tag to place the async pipe on you can use the ng-container and place the async pipe with the variable name on it.
Now let's update the TypeScript by passing an array to the Observable:
this.testService.updateTestBehaviorSubject([{title:'Some title', description:'Test description'}, {title:'Some title', description:'Test description'}]);
You can also use the async pipe in combination with the *ngFor loop in your HTML template like this:
Handling multiple async pipes
The last thing is how you can handle multiple async pipes in one HTML template. Let's say we have 3 different observables in our HTML template and all are used inside a ngIf like this:
When the number of observables grows the complexity of the HTML template does as well. This can result in UI bugs where some elements show up before others or don't get their async value because it's inside another one that resolves too late and so on. There is a cleaner way of handling this by creating an ng-container element where you resolve all the async pipes in one go and assign it to a variable.
Conclusion
Using the Angular async pipe is a great way to reduce boilerplate code, create some consistency in the way you handle observables and it minimizes the change of data leaks. I would recommend using async pipes whenever possible, it will improve the performance and maintainability of your application. Using the async pipe in combination with OnPush change detection even makes things better by automatically marking the HTML template to be checked for changes when a new value is received.