Home > Enterprise >  rxjs trigger one observable before the other one conditionaly
rxjs trigger one observable before the other one conditionaly

Time:02-08

I am seeking for help or a clue for an issue with rxjs that I am not able to solve.

Basically, I want to trigger an observable and complete before the original one trigger.

Case: I am working on a project in which I have two profiles, say profileA, profileB. Sometimes profileA has some properties that I need to pass to the profileB in order to get the profileB. And at the end, I combine the both profiles and return the orignal observable.

Usually, I cache the profileA in localStorage and I can get the profileB.

getProfileB() {
  const url = `${..}/xyz/${this.profileB.username}/userData`
  return this.http.get(anUrl); // and returns the observale to the next destination 
}

But sometimes could be that the profileA is not available in the localstorage, so in that case, first I have to find fetch the profileA and then continue with the profileB fetching.

I would like to use the following approch. Since I don't about

getProfileA() {
  return this.http.get(anUrlForProfileA);
}

getProfileB() {
  const url = `${..}/xyz/${this.profileB.username}/userData`
  return this.http.get(anUrl).pipe(
    aPipeOperator() => {
    // the null could be the mean that continue with the current Observable, whereas concatenateFirst means, first completes the getProfileA observable and then continue with the original one.
      return this.profileA?.username?.length > 0? concatenateFirst(this.getProfileA()) : null;

     
    }
  ); 
}

I am little struggling with the rxjs, and it is also difficult for me to explain, so please help me. This issue could solve me other problems too.

Thanks

CodePudding user response:

[Edit]: Since you want to use your profileA in your profileB request, you should use SwitchMap, as Jason Goemaat said. Here is another example a bit different for a better beginner comprehension than his one :

export class AppComponent implements OnInit {
  ngOnInit() {
    this.getProfileB();
  }
  
  getProfileA(): Observable<{ name: string, profile: string }> {
    return of({ profile: "A", name: 'Quentin' });
  }

  getProfileB() {
    this.getProfileA().pipe(
      switchMap(profileA => {
        // ProfileA is completed, you can acces its value
        console.log(profileA);
        // return another observable (For you, your api call for ProfileB)
        return of({profile: "B", name: "John", profileA});
      }),
    ).subscribe(profileB => {
      // Get profileB value here
      console.log(profileB);
    })
  }
}

From your example, it looks like you could use forkjoin(). It wait for all the observables to complete before to continue, so you could so something like that:

forkJoin(getProfileA(), this.http.get(anUrlForProfileB)).pipe(
  mergeMap(obs => { //... use obs[0] and obs[1] to access your observable values}
);

CodePudding user response:

I think switchMap is what you are looking for. You pass it a function that will take the result of your observable and return an observable to replace it.

const getProfileA = () => of({ name: 'A' })

const getProfileB = (profileA: any) => {
  const requestProfileB = of({ name: 'B', message: `This is profile B, using data from profile with name '${profileA.name}'`})
  return requestProfileB.pipe(map(profileB => ({ profileA, profileB })))
}

getProfileA().pipe(switchMap(getProfileB)).subscribe(x => console.log(x))

Here getProfileA mimics an http call to return profile 'A'. That result is piped into switchMap which you pass getProfileB. That function takes profileA and creates requestProfileB using data from profileA. That is piped to map() which combines the result from the request for profileB with the passed value for profileA into one object that will be emitted when the result is subscribed to. Result:

{
    "profileA": {
        "name": "A"
    },
    "profileB": {
        "name": "B",
        "message": "This is profile B, using data from profile with name 'A'"
    }
}

CodePen Link

  •  Tags:  
  • Related