Reputation: 462
this.route.params.pipe(
concatMap((param) => {
const ein = param['nonprofit-id'];
return this.nonprofitService.getNonprofit(ein).pipe(
concatMap((nonprofit) => {
this.nonprofit = nonprofit;
const ratingID = nonprofit.currentRating?.ratingID ? nonprofit.currentRating.ratingID : -1;
return this.nonprofitService.getRatingForNonprofit(ein, ratingID).pipe(
concatMap((nonprofitRating) => {
this.nonprofitRating = nonprofitRating;
const causeID = this.nonprofit?.cause?.causeID ? this.nonprofit?.cause?.causeID : 0;
return this.nonprofitService.getSimilarNonprofits(causeID);
})
);
})
);
})
).subscribe((response) => {
this.similarNonprofits = response;
});
I would like to know if the above is the correct way to chain concatMaps in Angular RxJS. The other two calls rely on the "nonprofit" in question being retrieved so that its object can be returned. Rating and Similar Nonprofits can be retrieved at the same time so I was thinking there would be some way I could do this without nesting those concatMaps within each other.
Upvotes: 1
Views: 1300
Reputation: 31105
Continuing from @martin's answer, I'd suggest to use forkJoin
to combine the last two concatMap
s to a single request since they do not depend on each other. This would also trigger the requests in parallel and might help in improving performance (though miniscule).
I've also replaced the ternary operator with the nullish coalescing operator ??
.
this.route.params.pipe(
concatMap((param: any) => {
const ein = param['nonprofit-id'];
return this.nonprofitService.getNonprofit(ein).pipe(
map((nonprofit) => ([ein, nonprofit]))
);
}),
concatMap(([ein, nonprofit]) =>
forkJoin([
this.nonprofitService.getRatingForNonprofit(ein, (nonprofit.currentRating?.ratingID ?? -1)),
this.nonprofitService.getSimilarNonprofits(ein, (this.nonprofit?.cause?.causeID ?? 0))
]).pipe(
map(([nonprofitRating, similarNonprofits]) => ([
nonprofit,
nonprofitRating,
similarNonprofits
]))
)
)
).subscribe({
next: ([nonprofit, nonprofitRating, similarNonprofits]) => {
this.nonprofit = nonprofit;
this.nonprofitRating = nonprofitRating;
this.similarNonprofits = similarNonprofits;
},
error: (error: any) => {
// handle error
}
});
You could also define an object with the properties to avoid using destructuring syntax with multiple elements.
Upvotes: 2
Reputation: 96891
If you need to be using all the previous values it usualy ends with nesting more and more chains but in this case it seems already too complicated so you can map()
each inner Observable into an array (or object) like the following:
this.route.params.pipe(
concatMap((param) => {
const ein = param['nonprofit-id'];
return this.nonprofitService.getNonprofit(ein).pipe(
map(nonprofit => [ein, nonprofit]),
);
),
concatMap(([ein, nonprofit]) => {
const ratingID = ...;
this.nonprofitService.getRatingForNonprofit(ein, ratingID).pipe(
map(nonprofitRating => [nonprofitRating, ein, nonprofit]),
);
}),
concatMap(([nonprofitRating, ein, nonprofit]) => {
const causeID = ...;
return this.nonprofitService.getSimilarNonprofits(causeID).pipe(
map(similar => [similar, nonprofitRating, ein, nonprofit]),
);
}
).subscribe(([similar, nonprofitRating, ein, nonprofit]) => { ... });
Obviously, you don't have to unwrap each array passed as a parameter to consecutive concatMap
s but I hope you get the point.
Upvotes: 4