Jadenkun
Jadenkun

Reputation: 327

How to avoid using nested observables Angular 8

I've created my own db.json server using typicode/json-server that looks like this -

 {
    "cart": [
        {
          "cartId": 1,
          "products": [
            { "pId": 1, "name": "aaa", "quantity":.. },
            { "pId": 2, "name": "bbb", "quantity":.. }
          ]
        },
        {
          "cartId": 2,
          "products": [{ "pId": 2, "name": "bbb", "quantity":.. }]
        }
      ]
}

This is the method to add a product to a cart:

addProductToCart((productObj:any)) {
    this.getCart() // uses POST, reference '/cart', returns observable
    .subscribe(cartId => {
      this.getProducts(cartId, productObj.pId) // uses GET, reference '/cart/cartId/pId', returns observable
      .subscribe(product => {
        // update properties of product here..
      });
    });
  }

I'm new to Angular and I understand that nested observables are not best-practice.

How can I avoid using nested observables?

Is there a way to write the same method using a different approach?

Upvotes: 0

Views: 261

Answers (1)

mortom123
mortom123

Reputation: 556

I would advise looking into the mapping operators provided by RxJS.

There are 3 types (that i know of): switchMap, concatMap, mergeMap

Each of maps an observable emission to another observable which is then subscribed to and propagated to the outer subscription handler.

You may ask now, if they all do the same, why are there 3 different names for it? Well, all of them do it a little differently. ;)

First - switchMap: observables are streams, so for each new emission switchMap, turns it into another (inner) observable, but when the outer observable emits a new value, earlier inner subscriptions are cancelled, therefore only 1 inner subscription at a time is possible.

mergeMap : like the name implies, it "merges" all inner observables into the stream and emits them without any special order.

concatMap : almost like mergeMap but it respects order, so items emitted by the outer stream, also get emitted in the same order from the inner observable.

When to use what?
For order-dependent streams you obviously want to use concatMap, if you value throughput more than order go for mergeMap. switchMap is best for searches and streams where you "don't care" about older values, be careful tho, since switchMap cancels inner observables http calls might not get completed.

Of course there's a lot more stuff to discover in RxJs (e.g. resultSelectors for the mapping operators). This is just intended as a brief overview.

Further reading:
https://medium.com/@luukgruijs/understanding-rxjs-map-mergemap-switchmap-and-concatmap-833fc1fb09ff
https://www.learnrxjs.io/operators/transformation/switchmap.html
https://www.learnrxjs.io/operators/transformation/mergemap.html
https://www.learnrxjs.io/operators/transformation/concatmap.html
RxJs docs. (they have nice marble diagrams)

Now for your example:

addProductToCart(productObj: any) {
    this.getCart()
        .pipe(
            switchMap(cartId => this.getProducts(cartId, productObj.pId))
        )
        .subscribe(product => {
            // update properties of product here..
        });
}

I assume getCart() only emits the cart once and we dont need to worry about cancelling the inner subscription. You could also use any other of these operators if the assumption holds.

Upvotes: 3

Related Questions