Search code examples
javascriptangularrxjsrxjs-observables

Nested subscribe that depends on outer subscribe response


Currently i have a code that looks like this:

TS:

ngOnInit() {
this.authService.getCurrentUser().subscribe((response) => {
      this.currentUser = response;
      const position = this.appService.searchPosition();
      const formData = this.appService.getFormData(this.candidateId);
      forkJoin([position, formData]).subscribe({
        next: (data) => {
          [this.positionList, this.myForm] = data;
          this.myForm.patchValue({
             // Initiating form with values from formData
             phoneNumber: formData.phoneNumber
          });
         
          // Here I want to add another service call to check for a status
          
          this.appService.checkStatus(formData.phoneNumber, this.candidateId).subscribe(
            response => {

               // Response here is a boolean
               if (response) {
                  this.status = true;
               }
            }
          )

          this.isLoading = false;
        },
        error: (err) => console.error(err),
      });
    });
}

HTML:

<button *ngIf="status">Some Button</button>

The issue I am facing is that this.status does not update because the nested subscribe is fired after the view got initiated with this.isLoading = false.

Putting this.isLoading = false inside nested subscribe is not an option because the view won't initiate at all.

How should I call a nested subscribe in this case so that this.isLoading = false works correctly here ?

Have tried switchMap and mergeMap as it was suggested in other threads. Didn't work, and I'm pretty sure it's because of how I tried to implement them.

This was my attempt:

this.authService.getCurrentUser().pipe(
 switchMap((user) => {
   this.currentUser = user

   // This call gets formData for next switchMap
   return this.appService.getFormData(this.candidateId);
  }
 ),
 switchMap((formData) => {

   // This call gets status
   reutrn this.appService.checkStatus(formData.phoneNumber, this.candidateId)
 })
).subscribe(data => {

   // Console log is not called and rest of the code doesn't work.
   console.log(data); 
 })

I know one of those is most likely the right answer for me, but I simply can't get to understand how should I use them.


Solution

  • You are chaining unnecessary observables from the beginning, take a look at this:

    this.authService.getCurrentUser().subscribe(response => {
      this.currentUser = response;
      ...
      ...
    })
    

    The only purpose of getCurrentUser() is to store data in the class property currentUser, after that, the code that follows does not even use currentUser value, then why searchPosition and getFormData need to be embedded in this first observable? These two do not depend on the response from getCurrentUser(), so primarily, you could start handling the user separately with something like:

    // do not forget to handle the subscription
    this.authService.getCurrentUser().subscribe(res => this.currentUser = res);
    

    I see a few more things in your code that make me wonder the logic shown in this post, so I will try to figure out what you want to achieve. For handling the other observables, you could do it as the following:

    const position$ = this.appService.searchPosition();
    const formData$ = this.appService.getFormData(this.candidateId);
    const final$ = forkJoin([position$, formData$]);
    
    final$.pipe(
      switchMap((res) => {
        const [positionData, formData] = res;
        this.positionList = positionData;
        this.myForm.get('phoneNumber').patchValue(formData.phoneNumber); 
        
        return this.appService.checkStatus(formData.phoneNumber, this.candidateId);
      })
    ).subscribe((response) => {
      this.isLoading = false;
      if (response) this.status = true;
    })
    

    Another alternative without using forkJoin could be as the following:

    this.appService.searchPosition().pipe(
      switchMap((response) => {
         this.positionList = response;
         return this.appService.getFormData(this.candidateId);
      }),
      switchMap((response) => {
        this.myForm.get('phoneNumber').patchValue(response.phoneNumber); 
        return this.appService.checkStatus(response.phoneNumber, this.candidateId);
      })
    ).subscribe((response) => {
      this.isLoading = false;
      if (response) this.status = true;
    });