Now you’ve learned the basics of Angular’s NgFor it’s time to take things up a notch and introduce some Observables. In this article you’ll learn how to use Observables with Angular’s NgFor directive and the async pipe.
NgFor has a not-so-obvious feature that lets us will help us deal with asynchronous operations – the async pipe. The async pipe takes care of subscribing/unsubscribing to Observable streams for us.
Let’s explore how we can handle NgFor alongside the async pipe to subscribe to an Observable!
Using ngFor
Here’s the data we’ll be using as our source to pass into *ngFor
:
interface Pizza {
id: string;
name: string;
price: number;
}
const getPizzas$: Pizza[] = [
{ id: "j8P9sz", name: "Pepperoni", price: 899 },
{ id: "tMot06", name: "Supreme", price: 999 },
{ id: "x9sD3g", name: "Sizzler", price: 899 },
];
And here’s where we introduce NgFor alongside perhaps another local NgForOf variable such as index
:
<ul>
<li *ngFor="let pizza of pizzas; index as i">
{{ i + 1 }}. {{ pizza.name }}
</li>
</ul>
But Angular is reactive, this means it’s all about Observables. This pattern of Angular development doesn’t follow an Observable data source pattern. So, let’s bring them in!
ngFor and Async Pipe
For this, we’ll introduce Observable of() from RxJS and demonstrate subscribing to an Observable stream via the async pipe and update our typings:
import { Observable, of } from "rxjs";
const getPizzas$: Observable<Pizza[]> = of([
{ id: "j8P9sz", name: "Pepperoni", price: 899 },
{ id: "tMot06", name: "Supreme", price: 999 },
{ id: "x9sD3g", name: "Sizzler", price: 899 },
]);
🕵️♀️ RxJS of()
is an Observable creation operator that emits the value we pass in!
Let’s get the getPizzas$
variable into our component:
@Component({
selector: 'app-root',
template: `...`
})
export class AppComponent implements OnInit {
pizzas$: Observable<Pizza[]>;
constructor() {}
ngOnInit() {
this.pizzas$ = getPizzas$;
}
}
🔭 Tip: it’s common practice to suffix properties that are Observables with a $
, such as pizzas$
Our NgFor directive can now use the async pipe to subscribe to the pizzas$
observable:
<ul>
<li *ngFor="let pizza of pizzas$ | async; index as i">
{{ i + 1 }}. {{ pizza.name }}
</li>
</ul>
It’s important to remember that using | async
creates a subscription if the source is an Observable. Thankfully, when the component is destroyed the subscription is also managed and unsubscribed for us!
Check out the live StackBlitz example:
NgFor Template and Async Pipe
When we think of NgFor, we should think of NgForOf. Here’s how you can use NgFor with the <ng-template>
element using the async pipe:
<ul>
<ng-template ngFor [ngForOf]="pizzas$ | async" let-pizza let-i="index">
<li>{{ i + 1 }}. {{ pizza.name }}</li>
</ng-template>
</ul>
This is the longer syntax using <ng-template>
and explains why we use *ngFor
with the asterisk, which denotes that it’s a behavioral directive.
Check out the live StackBlitz example:
NgFor + NgIf + Async Pipe
So far we’ve covered some really nice usage of NgFor with the async pipe, but now we’re going to show a common practice of “unwrapping” observables on different levels of the template.
First, using NgIf and the Async Pipe, we can do this:
<div *ngIf="pizzas$ | async as pizzas">
{{ pizza }}
</div>
Instead of using the async pipe directly with NgFor, we could use NgIf to unwrap our Observable into a pizzas
variable.
This would allow us to reuse the variable multiple times inside the template without creating repeat subscriptions:
<div *ngIf="pizzas$ | async as pizzas">
<p>🍕 {{ pizzas.length }} Pizzas!</p>
<ul>
<li *ngFor="let pizza of pizzas; index as i">
{{ i + 1 }}. {{ pizza.name }}
</li>
</ul>
</div>
This also means we can avoid using things like the safe navigation operator ?
with NgIf or NgFor.
Check out the live StackBlitz example:
Summary
We’ve covered a lot of ground here using Angular’s NgFor directive and the async pipe.
Starting with static data we then used Observable of()
to create an Observable and async pipe it into NgFor.
From there, we looked at how we could potentially use NgIf alongside NgFor (and the rest of our template) to avoid subscribing multiple times to our Observable.
I am having problem with ng*For where my code is like this ngfor=”let p of person”
<h1>p.name</h1>
<h1>p.surname</h1>
It is duplicating I dont know what is the cause.Thanks!