0

In angular i want to create a multiple subscribe sequence. When the first subscribe is complete do next subscribe. Similar to this.

this.globalCtrl.getSon().subscribe(
                  response => {
                    this.son= response;
                  },
                  error => {
                    console.log(error);
                  },
                  () => {
                   //DOESN´T ENTER IN THIS SUBSCRIBE
                    this.dashboardCtrl.getMarkers(this.son).subscribe(

                      response => {
                        this.markers = response["body"]["data"];
                        if (this.markers.length == 0) {
                          this.toast.fire({
                            type: "error",
                            title: "No hay datos geográficos del grupo seleccionado"
                          });
                        }
                        this.timezone = response["body"]["timezone"];
                      },
                      error => {
                        console.log(error);
                      }
                    );

The problem: Doesn't enter in the second subscription and the 'response' has data. ¿Anyone know how can i do that?

UPDATE

If i put second subscription inside response like this

  this.globalCtrl.getSon().subscribe(
                      response => {
                        this.son= response;
                        this.dashboardCtrl.getMarkers(this.son).subscribe(

                          response => {
                            this.markers = response["body"]["data"];
                            if (this.markers.length == 0) {
                              this.toast.fire({
                                type: "error",
                                title: "No hay datos geográficos del grupo seleccionado"
                              });
                            }
                            this.timezone = response["body"]["timezone"];
                          },
                          error => {
                            console.log(error);
                          }
                        );

Works but view doesn´t show data. When i refresh the view data load properly, but in first load doesn´t load data.

UPDATE 2: USING SWITCH MAP

  this.globalCtrl.getfather().pipe(
      switchMap(son=> this.dashboardCtrl.getMarkers(son.toString()).subscribe( /ERROR
        response => {
          this.markers = response["body"]["data"];
          if (this.markers.length == 0) {
            this.toast.fire({
              type: "error",
              title: "No hay datos geográficos del grupo seleccionado"
            });
          }
          this.timezone = response["body"]["timezone"];
        },
        error => {
          console.log(error);
        }
      )
    );

The propierty subscribe doesn´t exist in OperatorFunction

Maybe the observable cause the error

 getFather(): Observable<any> {
    return this.grupo.asObservable();
  }
El Hombre Sin Nombre
  • 2,906
  • 18
  • 49
  • 92
  • Possible duplicate of [Angular - two subscriptions in ngOnInit result in object 'undefined'](https://stackoverflow.com/questions/55638147/angular-two-subscriptions-in-ngoninit-result-in-object-undefined) – wentjun Jul 26 '19 at 07:34
  • 1
    You shouldn't be nesting multiple `subscribe()` methods within the same `subscribe()` block! It is very bad practice when it comes to handling observables. Instead, you should be making use of RxJS operators such as `mergeMap`, `switchMap`, or even `forkJoin`, depending on the requirements and flow of data. You may refer to this as well. [Angular Subscribe within Subscribe](https://stackoverflow.com/questions/55447803/angular-subscribe-within-subscribe/55447947#55447947) – wentjun Jul 26 '19 at 07:35

4 Answers4

2

It is recommended that you do not nest a subscribe inside another subscribe. It makes it difficult to manage and just about impossible to correctly unsubscribe. Instead, use switchMap.

Here is an example from one of my sample apps:

  todosForUser$ = this.http.get<User>(`${this.userUrl}/${this.userName}`)
    .pipe(
      switchMap(user =>
        this.http.get<ToDo[]>(`${this.todoUrl}?userId=${user.id}`)
      )
    );

You can see this code here:

https://stackblitz.com/edit/angular-todos-deborahk

Using your code, it would be something like this (NOT syntax checked):

import { switchMap } from 'rxjs/operators';

this.globalCtrl.getSon()
 .pipe(
    switchMap(son => this.dashboardCtrl.getMarkers(son))
 ).subscribe(...);

UPDATE: Simplified initial example, which originally demonstrated how to handle multiple related datasets.

DeborahK
  • 57,520
  • 12
  • 104
  • 129
  • Be sure to import it as shown above, and be sure to spell it `switchMap` (little 's') – DeborahK Jul 26 '19 at 07:48
  • 1
    This should be the accepted answer. The other answers make use of nested `subscribe()`, which isn't the Angular/RxJS way of doing things – wentjun Jul 26 '19 at 07:53
  • Looks like your parentheses aren't quite right: `switchMap(son=> this.dashboardCtrl.getMarkers(son))).subscribe` You need to close the `pipe()` before subscribing. See above that I have *3* closing parens. – DeborahK Jul 26 '19 at 08:07
  • switchMap will work but for his case mergeMap would fit better. – Dino Jul 26 '19 at 08:43
  • I had been using `mergeMap` as well ... but *every* example I found anywhere that discussed nested subscribes use `switchMap`. I normally only used switchMap if I really want to switch (like when getting data on user button clicks). – DeborahK Jul 26 '19 at 17:16
  • @DeborahK would u check this link : https://stackoverflow.com/questions/58432169/rxjs-subscribe-inside-subscribe-with-each-level-returns-values-and-store-into-re . i'm stuck with 4 level Nested Subscription – Sarath Mohandas Oct 18 '19 at 05:58
1

Your second subscription is put in a wrong place. It should be inside the response of a first subscription

this.globalCtrl.getSon().subscribe(
                  response => {
                    this.son= response;
                    // ADD THE OTHER SUBSCRIBE HERE
                  },
                  error => {
                    console.log(error);
                  }

The observable callback has 3 types - next, error, complete

You were putting the second subscription in a complete callback and that was the reason it was never called (Your first observable never completed).

Read more about Observables here

Dino
  • 7,779
  • 12
  • 46
  • 85
1

You have to move your second subscription inside your first one:

this.globalCtrl.getSon().subscribe(
              response => {
                this.son= response;
                  this.dashboardCtrl.getMarkers(this.son).subscribe(
                  response => {
                    this.markers = response["body"]["data"];
                    if (this.markers.length == 0) {
                      this.toast.fire({
                        type: "error",
                        title: "No hay datos geográficos del grupo seleccionado"
                      });
                    }
                    this.timezone = response["body"]["timezone"];
                  },
                  error => {
                    console.log(error);
                  }
              },
              error => {
                console.log(error);
              } 
                );
Luis Rico
  • 625
  • 6
  • 22
1

Having a subscription inside a subscription is called Flattening

In response to @DeborahK answer using switchMap would work, but it would be a misuse of it. switchMap would be a way to go if your first observable could change while there's a subscription already to it. Better approach would be to use mergeMap also (previously) known as flatMap

mergeMap - Use it when you have an Observable whose results are another Observable

switchMap - Use it when you have an Observable that can change and whose results are another Observable

If your first Observable emits something, mergeMap wouldn't discard the previous Observable, therefore your inner subscription would still be subscribed to the previous one. In switchMap it would discard the previous Observable and inner subscription would subscribe to the inner one.

The solution:

import { mergeMap } from 'rxjs/internal/operators/mergeMap';
.......
.......

this.globalCtrl.getSon().pipe(
  mergeMap((response) => {
    this.son = response;
    return this.dashboardCtrl.getMarkers(this.son)
  })
).subscribe((res) => {
  this.markers = response["body"]["data"];
});
Dino
  • 7,779
  • 12
  • 46
  • 85
  • what if there is more than two level? Please Check this link.https://stackoverflow.com/questions/58432169/rxjs-subscribe-inside-subscribe-with-each-level-returns-values-and-store-into-re – Sarath Mohandas Oct 18 '19 at 06:07