Home > Net >  Angular and RxJS - merge multiple HTTP requests, but emit the outer one instantly
Angular and RxJS - merge multiple HTTP requests, but emit the outer one instantly

Time:11-25

I have a hard time wrapping my head around some aspects of RxJS operators. I have some code here (in a component):

extendedOrders$ = this.salesService.getOrders().pipe(
  switchMap(orders => {
    return forkJoin(orders.map(order => {
      return this.salesService.getOrderCustomers(order.id).pipe(
        map(customer => ({ ...order, customerName: customer.name })),
      );
    }));
  }),
);

It is possibile extend it and do more then one call inside the forkJoin? For example, another call to another getById service and merge the response in the same object?

UPDATE

My first try:

this.salesService.getOrders().pipe(
  switchMap(orders => {
    return forkJoin(
      orders.map(order => {
        return {
          ...order,
          idAttr1: this.service1.method1(order.idAttr1).pipe(
            map(result => result.name)
          ),
          idAttr2: this.service2.method2(order.idAttr2).pipe(
            map(result => result.name)
          ),
        };
      })
    );
  })
)
.subscribe((result) => {
  console.log(result);
});

CodePudding user response:

I believe you are getting confused by all the nested functions here. At least I am ;) I suggest to break all of this down a bit. Use a helper function:

this.salesService.getOrders().pipe(
  switchMap(orders => 
    forkJoin(
      orders.map(order => loadOrder(order))
    )
  )
)
.subscribe((result) => console.log(result));

Now loadOrder can do whatever it needs to retrieve a Order (which I assume is the type of order).

In your first example this would be:

function loadOrder(order: Order): Observable<Order> {
  return this.salesService.getOrderCustomers(order.id).pipe(
    map(customer => ({ ...order, customerName: customer.name })),
  );
}

Now you can expand on this, the function gives you an outline of inputs and outputs, reduces nesting and increases readability. Probably you want this:

function loadOrder(order: Order): Observable<Order> {
  // Observable to load idAttr1
  const idAttr1 = this.service1.method1(order.idAttr1).pipe(
    map(result => result.name)
  );
  // Observable to load idAttr2
  const idAttr2 = this.service2.method2(order.idAttr2).pipe(
    map(result => result.name)
  );

  // retrieve both via forkJoin and insert them into order
  return forkJoin({idAttr1, idAttr2}).pipe(
    map(({idAttr1, idAttr2}) => ({...order, idAttr1, idAttr2})
  );
}

CodePudding user response:

You can do something like this for the forkJoin method:

forkJoin({
stream1: this.service.method1(),
stream2:  this.service.method2()
})
.pipe(map(data => {
 const mergedObject = { ...data.stream1, ...data.stream2);
 return mergedObject;
})

But if one request has an error code the other one won't pass the value as well, even if it returns code 200

  • Related