'Emit from Observable and then make additional REST API call for more detail
I have two methods which do HTTP calls to Rest APIs. One is getProducts() which will return a array of products and the other is getProductsDetail() which can take that array as parameter and will return the array of products with additional fields populated.
I would like to call these two APIs in sequence, with getProductsDetail() depending on the result of getProducts(), but i would like my Observable to emit the result from getProducts() as soon as that is ready. And then have getProductsDetail() emit with the extra detail when this call returns.
Is there a way to do this with an RxJS operator like concatMap or mergeMap? I would like to have my template start to display as soon as possible, but fill in the extra data when this is available
UPDATE:
Hi, thanks for the replies! I'm sorry I only pseudo code at the moment, but I think my initial attempt was going to be be something similar to what Authur had below
initProductsPage(): Observable<Products[]>{
this.productsService.getProducts().pipe(
switchMap(
(products: Products[]) => {
// would like to also emit products here
return this.productsService.getProductsDetail(products);
}
)
}
My understanding would be that in this case a subscriber to initProductsPage() would only recieve an output when getProductsDetail() returns. I'd like to have that subscriber get the products while getProductsDetails() is executing.
Solution 1:[1]
Assuming both getProducts() and getProductsDetail() return an Observable<Product[]>, you could do something like this:
getProductsWithDetailsHydration(): Observable<Product[]> {
const productsShared$ = this.getProducts().pipe(share()); // <- to share same GET products request.
return merge(
productsShared$, // <- 1st obs Gets and emits Products
productsShared$.pipe( //<- 2nd obs Gets details using products response and emit Products with Details
concatMap((products) => this.getProductsDetail(products))
)
);
}
Cheers
Solution 2:[2]
Another approach would have been from API end is to have a getProductDetail$() method. Which can get details for a single product. Then you could easily use async in the template itself and fill the template.
Example Template Code
<ng-container *ngIf="getProduct$() | async as product">
// Some info on product
<div>{{product.title}}</div>
<ng-container *ngIf="getProductDetail$(product.id) | async as productDetail">
// Some info on product details
<div>{{productDetail.description}}</div>
</ng-container>
</ng-container>
Solution 3:[3]
I think that the better way to address this is by separating the two observables.
Then you could subscribe and get the values:
// Class variables
products: Products[];
products$: Observable<Products[]>;
productsDetails: ProductsDetails[];
productsDetails$: Observable<ProductsDetails[]>;
// Call OnInit or OnChanges depending of your needs
initProductsPage(): void {
// Initialize the observables
this.products$ = this.productsService.getProducts();
this.productsDetails$ = this.products$.pipe(
switchMap((products) => this.getProductsDetail(products))
);
// Using take(1) to complete the observable after it gets the value
this.products$.pipe(take(1)).subscribe(products => {
this.products = products;
});
// Using take(1) to complete the observable after it gets the value
this.productsDetails$.pipe(take(1)).subscribe(productsDetails => {
this.productsDetails = productsDetails;
});
}
Then you could use products and productsDetails in your template.
Also you may use the observables directly on the template with |async and in this case the second part of the initProductsPage() wont be neccesary.
// Class variables
products: Products[];
products$: Observable<Products[]>;
productsDetails: ProductsDetails[];
productsDetails$: Observable<ProductsDetails[]>;
// Call OnInit or OnChanges depending of your needs
initProductsPage(): void {
// Initialize the observables
this.products$ = this.productsService.getProducts();
this.productsDetails$ = this.products$.pipe(
switchMap((products) => this.getProductsDetail(products))
);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | akotech |
| Solution 2 | Nugu |
| Solution 3 |
