The built-in async pipe in Angular 2+ gives us a great tool to easily manage observable subscriptions. With it, we can learn to avoid having to manually subscribe to observables in component classes for most cases.
Let’s say we want to have an observable that gives us the current time every second. Without using the async pipe, we might do something like this:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/map';
@Component({
selector: 'app-root',
template: `Time: {{ time | date:'mediumTime' }}`
})
export class AppComponent implements OnInit, OnDestroy {
time: Date;
timeSub: Subscription;
ngOnInit() {
this.timeSub = Observable
.interval(1000)
.map(val => new Date())
.subscribe(val => this.time = val);
}
ngOnDestroy() {
this.timeSub.unsubscribe();
}
}
In our OnInit hook we created an observable that emits a value every second and maps that value to a new date. We then subscribed to that observable and set our time class variable to the emitted value. We also made sure to unsubscribe from the observable when the component is destroyed to clean up after ourselves.
In the template, we used the built-in date pipe to transform the date into our desired format of minutes and seconds.
It’s quite a bit of boilerplate code however, and if we forget to unsubscribe we run the risk of creating memory leaks. We can greatly simplify, and here’s the same functionality implemented using the async pipe instead:
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/map';
@Component({
selector: 'app-root',
template: `Time: {{ time$ | async | date:'mediumTime' }}`
})
export class AppComponent {
time$ = Observable
.interval(1000)
.map(val => new Date());
}
The async pipe takes care of subscribing and unwrapping the data as well as unsubscribing when the component is destroyed.
We can also use the async pipe to unwrap and pass data to an input for a child component:
<app-child [time]="time$ | async"></app-child>
And the child now has noting to do really but to display the data.
Let’s say we have a slightly more complex data structure available as an observable and we set an artificial delay of 1 second before getting its value (mimicking a network request):
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/delay';
@Component({ ... })
export class AppComponent {
cities$ = Observable
.of([
{name: 'Los Angeles', population: '3.9 million', elevation: '233′'},
{name: 'New York', population: '8,4 million', elevation: '33′'},
{name: 'Chicago', population: '2.7 million', elevation: '594′'},
])
.delay(1000);
}
In the template we can unwrap and subscribe to the data when it arrives with an ngFor directive like this:
<ul>
<li *ngFor="let city of cities$ | async">
Name: {{ city.name }},
Population: {{ city.population }},
Elevation: {{ city.elevation }}</li>
</ul>
Here’s an example making use of the ngIf structural directive. Our observable looks like this:
word$ = Observable.of('Abibliophobia');
And our template looks like this:
<span *ngIf="(word$ | async)?.length > 9; else shortWord">
Long word: {{ word$.value }}
</span>
<ng-template #shortWord>
Short word: {{ word$.value }}
</ng-template>
And we’ll get:
Long word: Abibliophobia
Notice how we pipe the word$ observable through async, but wrap it in parentheses to then be able to check the length on the unwrapped value. We also make use of the Elvis operator (?) to avoid an error when the value of word$ is not yet available.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!