3

I have a requirement where, once I get a response from my first API call, I need to make 2 API calls (can be in parallel). Once all the API call's response is got, I need to send the observables (obs2$ & obs3$) to the subcomponent.

Code is something like:

ngOnInit(): void {
    this.obs1$ = this.getData1();
    this.obs2$ = this.obs1$.pipe(
        concatMap(() => {
            return this.getData2();
        })
    );
    this.obs3$ = this.obs1$.pipe(
        concatMap(() => {
            return this.getData3();
        })
    );
}

private getData1(): Observable<string> {
    return this.service.getData1<string>('/api/get_1').pipe(
        tap((response: string) => {
            this.id = response
        })
    );
}

private getData2(): Observable<string> {
    return this.service.getData2<string>('/api/get_2/id='+this.id).pipe(
        tap((response: string) => {
            ...
            this.flag2 = true;
        })
    );
}

private getData3(): Observable<string> {
    return this.service.getData3<string>('/api/get_3/id='+this.id).pipe(
        tap((response: string) => {
            ...
            this.flag3 = true;
        })
    );
}

My .html is like this:

<ng-container *ngIf="(data2: obs2$ | async, data3: obs3$ | async) as data; else loading">
    <ng-container *ngIf="flag2 && flag3; else loading">
        <sub-component
            [data2]="data.data2"
            [data3]="data.data3"
        >
        <sub-component>
    </ng-container>
</ng-container>
<ng-template #loading> Loading... </ng-template>

The issue is API call of getData1 is getting called 2 times. Is there a way to have it in single. Also, if I can do it without using the flags (flag 2 & flag3). Note: I tried this, but it did not work:

ngOnInit(): void {
    this.obs1$ = this.getData1();
    this.obs1$.pipe(first()).subscribe(() => {
        this.obs2$ = this.getData2();
        this.obs3$ = this.getData3();
    });
}

2 Answers 2

5

You're almost there. Instead of triggering individual calls for data2 and data3, you could use the RxJS forkJoin function to trigger multiple observables in parallel.

You could also skip the variables id, flag2 and flag3 this way.

Try the following

ngOnInit(): void {
  this.obs$ = this.getData1().pipe(
    switchMap((id: string) => forkJoin({
      data2: this.getData2(id),
      data3: this.getData3(id)
    }))
  );
}

private getData1(): Observable<string> {
  return this.service.getData1<string>('/api/get_1');
}

private getData2(id: string): Observable<string> {
  return this.service.getData2<string>('/api/get_2/id=' + id);
}

private getData3(id: string): Observable<string> {
  return this.service.getData3<string>('/api/get_3/id=' + id);
}
<ng-container *ngIf="(obs$ | async) as data; else loading">
  <sub-component 
    [data2]="data.data2" 
    [data3]="data.data3"
  >
  <sub-component>
</ng-container>
<ng-template #loading> Loading... </ng-template>
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Michael. I was able to use your solution
1

If you need the obs2$ & obs3$ to be called on parallel with one HTTP request for obs1$, you can achieve that by adding the shareReplay operator to the obs1$.

Try it like the following:

ngOnInit(): void {
  this.obs1$ = this.getData1().pipe(shareReplay(1));
  this.obs2$ = this.obs1$.pipe(switchMap((id) => this.getData2(id)));
  this.obs3$ = this.obs1$.pipe(switchMap((id) => this.getData3(id)));
}

private getData1(): Observable<string> {
  return this.service.getData1<string>('/api/get_1');
}

private getData2(id: string): Observable<string> {
  return this.service.getData2<string>('/api/get_2/id=' + id);
}

private getData3(id: string): Observable<string> {
  return this.service.getData3<string>('/api/get_3/id=' + id);
}

Then regarding the flags, you can try the following within the template:

<ng-container *ngIf="{ data2: obs2$ | async, data3: obs3$ | async } as data; else loading">
  <ng-container *ngIf="data.data2 && data.data3; else loading">
    <sub-component [data2]="data.data2" [data3]="data.data3"></sub-component>
  </ng-container>
</ng-container>
<ng-template #loading> Loading... </ng-template>

5 Comments

Thanks Amer. I tried your solution, but, 'getData3' method is not called. I even used shareReplay(2).
It should work, please check it again if you missed anything.
I double checked. The issue is still present. Please note: I use tap to manipulate the observer (in all 3 getData methods) and I tried changing the sequence (interchanged getData2 and getData3). Still getData3 was not getting called
Thanks for your help. My mistake, I had missed to put async pipe for obs3$ in html.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.