Tip
Simplify Angular Templates that Use Observables with View Models
Learn how to use view models in your Angular templates to handle multiple observables in a cleaner and more organized way, reducing the need for multiple async pipes.
When you work with observables in Angular, your templates can get a bit messy because you find yourself passing the async pipe everywhere and then need to keep track of it.
You can see in this template that we have a lot of async pipes and we need to keep track of them.
<!-- home.page.html -->
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
{{ title$ | async }}
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<p>
I'm {{ name$ | async }} and I'm a {{ role$ | async }}, this is a random list of Pokemon:
</p>
<ion-list lines="none" class="ion-no-padding ion-no-margin">
<ion-item *ngFor="let pokemon of (pokemon# | async)">
<ion-label>
{{ pokemon.name }}
</ion-label>
</ion-item>
</ion-list>
</ion-content>
We can clean our template up a bit by using a view model in your template We'll do this by combining all the observables in your class into one single observable.
// home.page.ts
export class HomePage {
private readonly http = inject(HttpClient);
readonly title$ = of('Combining Observables');
readonly name$ = of('Jorge Vergara');
readonly role$ = of('Software Developer');
readonly pokemon$ = this.http.get<PokeResponse>('https://pokeapi.co/api/v2/pokemon').pipe(
map(response => response.results)
)
// add this observable to combine all the observables into one
readonly vm$ = combineLatest([this.title$, this.name$, this.role$, this.pokemon$]).pipe(
map(([title, name, role, pokemon]) => ({title, name, role, pokemon}))
)
}
Now back in our view we can move everything into an ng container and reference the view model.
<ng-container xngIf="vm$ | async as vm">
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
{{ vm.title }}
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<p>
I'm {{ vm.name }} and I'm a {{ vm.role }}, this is a random list of Pokemon:
</p>
<ion-list lines="none" class="ion-no-padding ion-no-margin">
<ion-item *ngFor="1let pokemon of vm.pokemon">
<ion-label>
{{ pokemon.name}}
</ion-label>
</ion-item>
</ion-list>
</ion-content>
</ng-container>