Search code examples
angularrxjsfork-joinsubject

Subscribe Subject only after getting data from forkJoin


I want to pass some data from one module to another module. For that I am using Subject. I have created below subject :

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SelectedClusterService {

  constructor() { }

  public selectedClusterName = new Subject<string>();

}

I am passing some data from "oneComponent" through above service like this and redirect to "anotherComponent":

this.selectedClusterService.selectedClusterName.next(cluster_name);

And fetching data to "anotherComponent" like this:

this.selectedClusterService.selectedClusterName.subscribe(res => {
  this.selectedCluster = res;
  console.log("this.selectedCluster", this.selectedCluster);
});

This is working fine, I am getting data to "anotherComponent". But In "anotherComponent" I am using forkJoin. I want to get data from subject only after getting data from forkJoin. I am trying like this:

   ngOnInit() {
    forkJoin([
      this.dataService.getUpgradeAppList(),
      this.dataService.getClusterList()
    ]).subscribe((data: any) => {
      console.log("forkJoin :: ngOnInit ::", data);
      this.appList = data[0].apps;
      this.clusterList = data[1];

    }); 
    this.selectedClusterService.selectedClusterName.subscribe(res => {
      this.selectedCluster = res;
      console.log("this.selectedCluster", this.selectedCluster);
    });
  }

I have tried ngAfterViewInit, ngAfterContentChecked with changeDetection also I try to use setTimeout, but nothing works. Always I am getting subject data first and then forkJoin data. How I can get forkJoin first and then subject data? Any help is appreciated...


Solution

  • First of all, there's no way to guarantee a sequence of getting data in your component as you work with async entities. I would use BehaviorSubject instead Subject to guarantee that you receive data from other component.

    Your service will be like:

    import { Injectable } from '@angular/core';
    import { BehaviorSubject } from 'rxjs';
    
    @Injectable({
      providedIn: 'root'
    })
    export class SelectedClusterService {
    
      constructor() { }
    
      public readonly selectedClusterName = new BehaviorSubject<string | undefined>(undefined);
    
    }
    

    Your component:

    ngOnInit() {
        forkJoin([
          this.dataService.getUpgradeAppList(),
          this.dataService.getClusterList()
        ]).
        pipe(
          tap((data: any) => {
            console.log("forkJoin :: ngOnInit ::", data);
            this.appList = data[0].apps;
            this.clusterList = data[1];
          }),
          switchMap(() => this.selectedClusterService.selectedClusterName),
          filter(res => res !== undefined)
        ).subscribe(res => {
          this.selectedCluster = res;
          console.log("this.selectedCluster", this.selectedCluster);
        });     
      }
    

    In your anotherComponent you can use the switchMap operator to start receiving data from oneComponent and the filter to ignore initial value in BehaviorSubject that we set to undefined.