问题
I am using Angular 6.
I have the following classes:
export class MyItem{
id: string;
name: string;
gizmo: Gizmo;
};
export class Gizmo{
id: string;
name: string;
color: string;
};
I have to make 2 API calls.
1 - To get the array of MyItems.
2 - To get the gizmo of a given MyItem.
I've tried this:
this.myItemService.get<MyItem[]>(
new FindParameters({ name: name })
).pipe(tap(z => {
z.forEach(function (item) {
this.gizmoService.get(item.id)
.subscribe(z => {
item.gizmo = z;
});
});
}))
.subscribe(x => this.totalCount = x.length);
However the gizmoService appears to not be in scope inside the function. (If I pull it out the service is fine)
So how can I load my object that will require 2 separate API calls?
回答1:
Calling .subscribe()
into a .subscribe()
is an anti-pattern and must be avoided at all cost. What you should do instead is merge your api calls into one single observable and get the end result with a .subscribe()
.
From what I see, here's how you should transform your observable to get your gizmo data into your item data:
this.myItemService.get<MyItem[]>(
new FindParameters({ name: name })
).pipe(
mergeMap((myItems: MyItem[]) => {
const gizmoRequestArray: Observable<MyItem>[] = myItems.map(item => {
return this.gizmoService.get(item.id).pipe(
map(z => {
item.gizmo = z;
return item;
})
);
});
return combineLatest(gizmoRequestArray);
})
)
.subscribe(x => this.totalCount = x.length);
回答2:
This should be pretty straight forward, just be aware that this doesn't preserve the same order of items.
this.myItemService.get<MyItem[]>(...)
.pipe(
mergeAll(), // flatten the array into single emission
mergeMap(item => this.gizmoService.get(item.id).pipe(
map(gizmo => {
item.gizmo = gizmo;
return item;
})
)),
toArray(), // turn all result into an array again
)
.subscribe(...)
来源:https://stackoverflow.com/questions/52705919/how-can-i-load-my-object-in-angular-rxjs-that-requires-2-api-calls