Search code examples
rxjsside-effectsngrx-effects

RxJs: How to combine two observable into a new observable with different type


I'm very new to RxJs and NgRx store , I'm want to create an @Effect with combination of two observables (very difficult to explain):

My interfaces looks like:

export interface ProductDetails {
  product: Product;
  productBody: ProductBody;
}

export interface ProductBody{
  id: string;
  body: string;
}

I'm trying to create a new objectproductDetails and set it's properties. properties are product which payload has product and productBody which gets it from productService(id) (It returns observable<productBody>)

This effect should return observable<productDetails>

   @Effect()
  getProductDetails$ = this.actions$
    .ofType(ProductActions.GET_STEP)
    .map(action => action.payload)
    .flatMap(s => {
      let body;
      this.productService.getStepBody(s.id).subscribe(x => body = x);
      return Observable.of({
        step: s,
        productBody: body
      });
    })
    .map(res => this.productActions.getProductDetailsSuccess(res));

this returns:Object {productBody: undefined, product: Object}

I understand why is returning undefined for productBody but not sure how to fix it. I tried using zip, switchMap and etc but no chance!


Solution

  • Here is what you can do.

    • You want to flatMap over the original observable to get the payload.id to give it to the getStepBody observable.
    • Then, inside the flatMap, you want to map on the getStepBody observable to return a new value that is a composite object of the payload and the response of the getStepBody.
    • Finally subscribe to get the final result.

    It looks something like this:

    getProductDetails$ = this.actions$
        .ofType(ProductActions.GET_STEP)
        .map(action => action.payload)
        .flatMap(payload => this.productService.getStepBody(payload.id).map(body => ({step: payload, productBody: body})))
        .subscribe(res => this.productActions.getProductDetailsSuccess(res));  //res is an object of {step: payload, productBody: body}
    

    If you are not wanting to return the whole object into a single function in the subscribe function, you could destructure the values to make them easier to consume separately. To that change the subscribe line to:

    .subscribe(({step, body}) => {//do something with each value});