Pipes and Observables
Let’s learn why pipes are useful when handling `Observables` in the template.
We'll cover the following
Asynchronous code in Angular is almost an inseparable technique used in this framework. So, the ability to handle it is crucial. When do asynchronous calls appear? The most common scenario is when we have to send an HTTP
request to display some data.
Observables in Angular
Let’s get back to the profile card task we implemented in the last tasks. The data context was simplified a bit compared to a standard application in which the user data needs to be fetched from an API before it’s displayed.
If we want the data context to be simplified, user data will no longer be supplied as a simple data object. Instead, it’ll be retrieved through another service. Because the fetch is asynchronous, the output of the service call will be an Observable User
rather than a User
object. Let’s look at the example below:
export class AppComponent implements OnInit{
user$: Observable<User>;
constructor(
private userService: UserService,
) {
}
ngOnInit() {
// get user from the service (as Observable)
this.user$ = this.userService.getUser();
}
}
The user$
property is Observable
, so we can’t simply use it to display the data in the template. Instead, we have to subscribe to the asynchronous data that finally comes and use that value to display the profile card.
The obvious way to do this is to create an additional property called user
and assign the value once Observable
emits it. The solution could look like this:
export class AppComponent implements OnInit{
user: User;
user$: Observable<User>;
constructor(
private userService: UserService,
) {
}
ngOnInit() {
// Get user from the service (as Observable)
this.user$ = this.userService.getUser();
// Subscribe to get the value
this.user$.subsribe(userResponse => { this.user = userResponse;
});
}
}
Now, we can use the value of the user
to display the profile card, and it will work.
However, this approach has several weaknesses, which are listed below:
-
It increases the state of the component.
-
The code is not very readable. There are a lot of lines and properties.
-
There may be memory leaks if we do not handle subscriptions correctly.
Therefore, we’ll accept the help of a built-in pipe to better handle Observables
.
The async
pipe
Angular introduces a pipe called AsyncPipe
precisely for cases where we have an Observable
value that we want to use in the template to render the data once the it returns. So, we have to remember that the data will come eventually when we use Observables
, but we have no control over when it happens. That’s why we need to handle it using AsyncPipe
.
The AsyncPipe
syntax is fairly straightforward. The pipe’s name is just async
, which we can see below:
Observable | async
Let’s assume that our User
is defined like this:
interface User {
name: string;
lastname: string;
}
To display its name, we need to handle the Observable
first and then get the name from the data.
<p> {{ ( user$ | async ).name }} </p>
The ( user$ | async )
fragment passes Observable
to AsyncPipe
, resolving the value (which is a user object) once it comes. Under the hood, it makes a subscription, just like we did in our component at the beginning of this lesson. However, the pipe takes excellent care and unsubscribes from it when the component is destroyed to prevent memory leaks.
Now, let’s think of what would happen if we also want to display the lastname
field below the name:
<p> {{ ( user$ | async ).name }} </p>
<p> {{ ( user$ | async ).lastname }} </p>
It renders the data correctly. However, there are two subscriptions for the created user, which result in two sent HTTP GET
requests. This is not ideal.
The as
keyword
The best option is still to use AsyncPipe
but call it only once, to prevent multiple subscriptions. Our well-known NgIf
directive has a powerful feature that can help us exactly in this case. Basically, when we use NgIf
in the template, we can not only check the statement for the boolean value, but also assign the value to a template variable. This is available through the as keyword. Let’s take a look at this syntax:
<div *ngIf="someData as data">
{{ data.value }}
</div>
In this example, the div
element is displayed when the someData
object has a true
value. But that’s not all! The value of the someData
object is assigned to the data
template variable. Therefore, this variable can be used in the template as {{ data.value }}
.
In the above case, it doesn’t make much sense to use this technique. However, using the as
keyword can be a powerful technique when it comes to the Observable
value. We can use NgIf
to assess the Observable
value resolved by AsyncPipe
as true, and then use the result as a template variable. Let’s look at the code below:
<ng-container *ngIf="user$ | async as user">
<p> {{ user.name }} </p>
<p> {{ user.lastname }} </p>
</ng-container>
The NgIf
directive resolves the asynchronous value with the AsyncPipe
and assigns it to the variable called user
. This variable is then gets the value of the user. If we do this, we can forget about all asynchronous issues because it’s a regular value now!
Below is the complete example of how this technique works:
Get hands-on with 1400+ tech skills courses.