Reputation: 3707
In my component template I am calling async
pipe for same Observable in 2 places.
Shall I subscribe to it and use returned array in my template or using async
pipe for same Observable in multiple places of template has no negative effect to performence?
Upvotes: 41
Views: 12732
Reputation: 153
You can make it like this, and in the template use the local variable
products$ = this._httpClient.get<Product[]>(`${this._environment.apiProducts}/product`)
.pipe(tap(data => console.table(data)),
share()
);
// Template
<ng-container *ngIf="products$ | async as products; else loading">
<div *ngFor="let product of products"></...>
<div *ngIf="products..."></...>
</ng-container>
I hope this post solves your problem
Upvotes: 0
Reputation: 559
Solution provided above by @Hinrich is very good however sometimes you are blocked because you want to use multiple observables in the template, in this case there is a simple solution like that (which work well for hot observables like a NgRx selector but maybe not well for a cold observable like http requests) :
@Component({
selector: "some-comp",
template: `
<ng-container *ngIf="{ book: squareData$ | async, user: otherData$ | async } as data">
Sub1: {{data.squareData}}<br>
Sub2: {{data.otherData}}<br>
</ng-container>
`
})
Upvotes: 2
Reputation: 1067
We use the @Hinrich solution but instead of the @pegaltier solution for multiple Observables, we use combineLatest()
.
this.data$ = combineLatest(book$, user$)
.pipe(
map(([book, user]) => {
return (book && user) ? { book, user } : undefined;
}),
);
<ng-container *ngIf="data$ | async as data">
{{ data.book }} {{ data.user }}
</ng-container>
Upvotes: 0
Reputation: 703
Another way of avoiding multiple subscriptions is to use a wrapping *ngIf="obs$ | async as someName"
. Using olsn's example
@Component({
selector: "some-comp",
template: `
<ng-container *ngIf="squareData$ | async as squareData">
Sub1: {{squareData}}<br>
Sub2: {{squareData}}<br>
Sub3: {{squareData}}
</ng-container>`
})
export class SomeComponent {
squareData$: Observable<string> = Observable.range(0, 10)
.map(x => x * x)
.do(x => console.log(`CalculationResult: ${x}`)
.toArray()
.map(squares => squares.join(", "));
}
It's also cool because it cleans out the template a bit too.
Upvotes: 18
Reputation: 884
I had better luck with .shareReplay from 'rxjs/add/operator/shareReplay' which is very new (https://github.com/ReactiveX/rxjs/pull/2443)
I also had luck with .publishReplay.refCount(1) (Angular 2 + rxjs: async pipe with .share() operator)
I'm honestly not sure about the difference between the two strategies. The comments in the PR for shareReplay suggest that there might be more risk for memory leaks of Subscriptions if not implemented correctly, so I might go with the .publishReplay.refCount(1) for now.
Upvotes: 2
Reputation: 16892
Every use of observable$ | async
will create a new subscription(and therefor an individual stream) to the given observable$
- if this observable contains parts with heavy calculations or rest-calls, those calculations and rest-calls are executed individually for each async
- so yes - this can have performance implications.
However this is easily fixed by extending your observable$
with .share()
, to have a shared stream among all subscribers and execute all those things just once for all subscribers.
Don't forget to add the share
-operator with import "rxjs/add/operator/share";
The reason why async-pipes don't share subscriptions by default is simply flexibility and ease of use: A simple .share()
is much faster to write than creating a completely new stream, which would be required if they were to be shared by default.
Here is a quick example
@Component({
selector: "some-comp",
template: `
Sub1: {{squareData$ | async}}<br>
Sub2: {{squareData$ | async}}<br>
Sub3: {{squareData$ | async}}
`
})
export class SomeComponent {
squareData$: Observable<string> = Observable.range(0, 10)
.map(x => x * x)
.do(x => console.log(`CalculationResult: ${x}`)
.toArray()
.map(squares => squares.join(", "))
.share(); // remove this line and the console will log every result 3 times instead of 1
}
Upvotes: 135