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.
1<!-- home.page.html -->
2<ion-header [translucent]="true">
3 <ion-toolbar>
4 <ion-title>
5 {{ title$ | async }}
6 </ion-title>
7 </ion-toolbar>
8</ion-header>
9
10<ion-content class="ion-padding">
11 <p>
12 I'm {{ name$ | async }} and I'm a {{ role$ | async }}, this is a random list of Pokemon:
13 </p>
14 <ion-list lines="none" class="ion-no-padding ion-no-margin">
15
16 <ion-item *ngFor="let pokemon of (pokemon# | async)">
17 <ion-label>
18 {{ pokemon.name }}
19 </ion-label>
20 </ion-item>
21 </ion-list>
22</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.
1// home.page.ts
2export class HomePage {
3 private readonly http = inject(HttpClient);
4
5 readonly title$ = of('Combining Observables');
6 readonly name$ = of('Jorge Vergara');
7 readonly role$ = of('Software Developer');
8
9 readonly pokemon$ = this.http.get<PokeResponse>('https://pokeapi.co/api/v2/pokemon').pipe(
10 map(response => response.results)
11 )
12
13 // add this observable to combine all the observables into one
14 readonly vm$ = combineLatest([this.title$, this.name$, this.role$, this.pokemon$]).pipe(
15 map(([title, name, role, pokemon]) => ({title, name, role, pokemon}))
16 )
17}
Now back in our view we can move everything into an ng container and reference the view model.
1<ng-container xngIf="vm$ | async as vm">
2 <ion-header [translucent]="true">
3 <ion-toolbar>
4 <ion-title>
5 {{ vm.title }}
6 </ion-title>
7 </ion-toolbar>
8 </ion-header>
9
10 <ion-content class="ion-padding">
11 <p>
12 I'm {{ vm.name }} and I'm a {{ vm.role }}, this is a random list of Pokemon:
13 </p>
14
15 <ion-list lines="none" class="ion-no-padding ion-no-margin">
16 <ion-item *ngFor="1let pokemon of vm.pokemon">
17 <ion-label>
18 {{ pokemon.name}}
19 </ion-label>
20 </ion-item>
21 </ion-list>
22 </ion-content>
23</ng-container>
Transcript
Here we have an example of an Angular application working with observables. It has some variables that we're printing on the page, and it's making an HTTP call to get some information from a server. When you work with observables, your templates can get a bit messy because you find
yourself passing the async pipe everywhere and then need to keep track of it. There is a pattern I learned from using NgRx component store and it's called using a view model in your template. For that, we're going to create a new class variable.
This is going to be our view model. Here we're going to combine all of these observables. What we want to do is
return one single object that has all of our information. Now, we can clean our template a bit by creating one single subscription to our view model. Now, we can move everything inside of an ng-container,
and now we can start using our view model. Since we are already using the async pipe here, we are not going to need it anywhere else. Instead, I'm going to get the variables from VM dot and the variable name.
You see, when I save, it's going to be exactly the same. I can reload the page and all of my data will still be there. With this, you only need to keep track of
one subscription and this is the one for your view model.