Reputation: 31649
I've got this code which uses two pipes to load a product instance and its playlist. If there's an error when loading the playlist, the property is set to null:
getProduct(productId: number): Observable<ProductDTO> {
return this.internalFindOne(productId).pipe(
flatMap((productDTO: ProductDTO) => {
return this.productPlaylistService.getProductPlaylist(productId).pipe(
map((productPlaylistDTO: ProductPlaylistDTO) => {
productDTO.playlist = productPlaylistDTO;
return productDTO;
}),
catchError(() => {
// If product playlist doesn't find anything, we still have to return the product dto object
productDTO.playlist = null;
return of(productDTO);
}),
);
})
);
}
It is already working this way. However, I don't think flatMap
should fit here. There's no compiler error, but as far as I know flatMap
works in a 1:n fashion, whereas map
does it in a 1:1 fashion. My code is expecting a productDTO
as an input here and returns the same productDTO
modified. But when I replace the flatMap
call by just map
, the nested map
function stops getting called (neither the success or the error parts).
ProductDTO
and ProductPlaylistDTO
are not iterable classes.
What am I misunderstanding here?
Upvotes: 1
Views: 1700
Reputation: 40612
flatMap's 'flat' prefix means that it flattens the observable it returns and as the result you'll get the returned observable data instead of the observable itself. It's like removing of one level of the nested observables. It works pretty similar to the switchMap. You can read about the difference here.
At the same time map just transforms the data, so if you return an observable from the map()
you'll get this observable as the result:
internalFindOne(productId).pipe(
flatMap(() => {
return getProductPlaylist(productId);
}),
tap((additionalData: ProductPlaylistDTO) => { ... })
);
internalFindOne(productId).pipe(
map(() => {
return getProductPlaylist(productId);
}),
tap((additionalData$: Observable<ProductPlaylistDTO>) => { ... })
);
But in your case, since these two requests don't depend on each other you can execute them simultaneously using combineLatest:
getProduct(productId: number): Observable<ProductDTO> {
return combineLatest([
this.internalFindOne(productId),
this.productPlaylistService.productPlaylistService(productId).pipe(
catchError(() => {
return of(null);
})
),
]).pipe(
map(([ product, playlist ]) => {
product.playlist = playlist;
return product;
})
);
}
Upvotes: 1