Daggle
Daggle

Reputation: 171

Angular 8 Nested subscribes

I'm doing a get request in order to retrieve the user role

private myOrders;
constructor(private userData: UserDataService) {
  this.userData.getUser().subscribe((user: any) => {
    //and then I'm doing a conditional get 
    if (user.role == "Admin") {
      this.userData.getAdminOrders().subscribe((adminOrders: any) => {
        this.myOrders = adminOrders
      });
    } else {
      this.myOrders = user.orders
    }
  });

My app shows properly only the user.orders (the orders for the normal customers) but when I'm with the Admin account it only shows the first page load but then they disappear.

How can I manage those nested subscribtion? (They are http.get on my userDataService)

Upvotes: 5

Views: 12222

Answers (3)

Chris
Chris

Reputation: 2397

Uses switchMap operator and observable iif and of

this.userData.getUser()
  .pipe(
    switchMap(({ role, orders }) =>
      iif(
        () => role === 'Admin',
        this.userData.getAdminOrders(),
        of(orders),
      ),
    ),
  )
  .subscribe(orders => (this.myOrders = orders));

never use a subscribe in another subscribe, because you create memory leacks, or for a cleaner and better solution, use async pipe as specified by @BizzyBob

Upvotes: 3

BizzyBob
BizzyBob

Reputation: 14740

You can avoid using nested subscriptions by using one of the "Higher Order Mapping Operators" LINK

This is just a fancy way of saying: operators that map the incoming value to another observable, subscribe to it, and emit those values. They also manage these "inner subscriptions" automatically.

The switchMap will "switch" to a new observable whenever a new value is received. So, when you pass in the "user", map it to the appropriate observable based on the user.role, it will subscribe to it and emit those values. When a new "user" value is received, it will unsubscribe from the previous observables, and subscribe to the new one.

Also, rather than subscribing in your component, you can define "myOrders" to be an Observable, which you can subscribe to in the template, like this:

public myOrders$ = this.userData.getUser().pipe(
    switchMap(user => { 
        return user.role = 'Admin' 
            ? this.userData.GetAdminOrders() 
            : of(user.orders));
        }
    );

Then, you can use the async pipe in your template. This means you don't need to worry about managing subscriptions in your component.

<ul>
    <li *ngFor="order of myOrders$ | async">{{ order.description }}</li>
</ul>

Upvotes: 3

German Quinteros
German Quinteros

Reputation: 1930

You should use one of the rxjs operators on this type of cases.

For this case I recommend you flatMap:

private myOrders;
constructor(private userData: UserDataService) {
  this.userData.getUser().pipe(flatMap(user: any) => {
    if (user.role == "Admin") {
      return this.userData.getAdminOrders();
    } else {
      return of(user.orders)
    }
  }).suscribe((result) => {
        this.myOrders = result
    }
  );

If user.role == "Admin" is true, result will be the result of the HTTP request in this.userData.getAdminOrders(). On the other case, the result will be user.orders.

Upvotes: 5

Related Questions