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'"
}
}