Reputation: 365
I have the following code which consists of multiple subscribes. What I need to achieve is like this :
My question : is there any better way to perform these nested subscription? is it a good practice to do like this?
this.activatedRoute.data.pipe(
map((data) => {
this.user = data['user'];
this.product = data['product'];
return this.product;
})
).subscribe(result => {
if (this.product === null) {
this.router.navigate(['/home']);
} else {
this.displayCurrency = this.dataService.getCurrencySymbolById(this.product.currency);
this.userService.getUser(this.product.createdBy).subscribe(seller => {
this.seller = seller;
this.ratingService.getRatingByUserId(seller.id).subscribe(rating => {
this.rating = rating;
})
});
}
});
Upvotes: 14
Views: 21817
Reputation: 1
You can use mergeMap
import { of, from } from 'rxjs';
import { map, mergeMap, delay, mergeAll } from 'rxjs/operators';
const getData = (param) => {
return of(`retrieved new data with param ${param}`).pipe(
delay(1000)
);
};
from([1, 2, 3, 4]).pipe(
mergeMap(param => getData(param))
).subscribe(val => console.log(val));
Upvotes: 0
Reputation: 42526
Technically, nesting subscribe works, but there is a more elegant and systematic way of handling this. You should really learn more about your RxJS operators.
First, we use mergeMap to map over the observable values from the activatedRoute into an inner observable.
Then, we use forkJoin to combine the observables into a single value observable, thus returning the value itself on the .subscribe()
this.activatedRoute.pipe(
tap(data => console.log(data)),
mergeMap(data => {
if (data.product === null) {
this.router.navigate(['/home']);
} else {
const getCurrency = this.dataService.getCurrencySymbolById(data.product.currency);
const getUsers= this.userService.getUser(data.product.createdBy);
const getRatings = this.ratingService.getRatingByUserId(seller.id)
return forkJoin(getCurrency, getUsers, getRatings);
}
})
).subscribe(res => {
console.log(res[0]); // currency
console.log(res[1]); // user
console.log(res[2]); // ratings
}
EDIT: Turns out I have misread the original question, as getRatingsByUserId is dependent on getUser. Let me make some changes. Either ways, I will leave the code above as it is, as it is good for OP's reference.
this.activatedRoute.data.pipe(
switchMap(data => {
this.user = data['user'];
this.product = data['product'];
return this.userService.getUser(this.product.createdBy);
}),
switchMap(data => {
if (this.product === null) {
this.router.navigate(['/home']);
} else {
this.seller = seller;
return this.userService.getRatingByUserId(this.product.createdBy);
}
})
).subscribe(res => {
console.log(res)
// handle the rest
})
Upvotes: 13
Reputation: 159
Use switchMap to switch to a new observable.
this.activatedRoute.data.switchMap((routeData) => {
this.user = routeData['user'];
this.product = routeData['product'];
return this.userService.getUser(this.product.createdBy);
}).switchMap(seller => {
this.seller = seller;
return this.ratingService.getRatingByUserId(seller.id);
}).subscribe(rating => {
this.rating = rating;
})
Upvotes: 1