I'm trying to use the declarative approach in Angular, unwrapping observables with the async
pipe and using OnPush
change detection.
I have a second observable, which may or may not be invoked, and needs data from the first observable to complete.
order.component.html
<div *ngIf="order$ | async as order">
<h3> Order {{ order.id }} </h3>
....
<app-returns *ngIf="order.status === 'Returned'"
[rma]="rma$ | async" >
</app-returns>
</div>
order.component.ts
order$: Observable<Order> = this.route.paramMap.pipe(
map((params: ParmaMap) => params.get('id')),
switchMap((id: number) =>
this.orderSvc.get(id)
));
rma$: Observable<Rma> = this.order$.pipe(
switchMap((order: Order) =>
this.rmaSvc.get(order.rma.id)
));
This issue with this approach is that the Order service makes two calls to the API to complete this.
When I have multiple observables needed in the template, I'll usually use a forkJoin
or combineLatest
to combine them. But in this case I only want to call the rma service if necessary.
Other, more naive approaches (while illustrating the issue well) have hilarious outcomes. Can you say 429?
<app-returns *ngIf="order.status === 'Returned'"
[rma]="rma$(order.rma.id) | async" >
</app-returns>
Typescript
rma$(id: number): Observable<Rma> {
return this.rmaSvc.get(id);
}
How do I pass a parameter while using the async pipe?
CodePudding user response:
you can change your order observable using shareReplay for caching to :
order$: Observable<Order> = this.route.paramMap.pipe(
map((params: ParmaMap) => params.get('id')),
switchMap((id: number) =>
this.orderSvc.get(id)
),
shareReplay(1)
);
and then create
rma$: Observable<Rma> = order$.pipe(
switchMap((order: Order) =>
this.rmaSvc.get(order.rma.id)
)