Home > Enterprise >  Angular - How to implement switchMap for multiple http requests?
Angular - How to implement switchMap for multiple http requests?

Time:04-02

In my project, I sometimes want to update the available options in a list when the user changes the selected value in another list. To do this, I've used valueChanges with the pipe operator and switchMap like this:

this.form.controls.typeControl.valueChanges
  .pipe(
    switchMap((typeId: number) => {
      return this.typesService.getProjectsByType(typeId);
    }),
  )
  .subscribe((projects) => {
    //...
  });

Now I have the problem that I need to perform two http requests at once to update multiple lists instead of just one. When I try to add a second switchMap, I get error TS2345: Argument of type 'OperatorFunction<number, Project[]>' is not assignable to parameter of type 'OperatorFunction<any, number>'. Here is how I tried to do this:

this.form.controls.typeControl.valueChanges
  .pipe(
    switchMap((typeId: number) => {
      return this.typesService.getProjectsByType(typeId);
    }),
    switchMap((typeId: number) => {
      return this.typesService.getProgramsByType(typeId);
    }),
  )
  .subscribe(([projects, programs]) => {
    //...
  });

How can I add a second http request here so I can process the data received by both request in the subscribe?

CodePudding user response:

You could use combineLataest or forkJoin to create an observable that emits when either of it's sources emit:

this.form.controls.typeControl.valueChanges.pipe(
    switchMap(typeId => combineLatest([
      this.typesService.getProjectsByType(typeId),
      this.typesService.getProgramsByType(typeId)
    ]))
  )
  .subscribe(([projects, programs]) => {
    // ...
  });

However, if these two piece of data (projectList and programList) aren't related to each other, you may find it more convenient to define them as separate observables:

private typeId$ = this.form.controls.typeControl.valueChanges;

projectList$ = this.typeId$.pipe(switchMap(id => this.typesService.getProjectsByType(id)));
programList$ = this.typeId$.pipe(switchMap(id => this.typesService.getProgramsByType(id)));

This can give you more granular control and allow you to use the async pipe in your template a little easier:

    <h1> Projects </h1>
    <ul> 
        <li *ngFor="let project of projectList$ | async">
            {{ project.name }}
        </li>
    </ul>

    <h1> Programs </h1>
    <ul> 
        <li *ngFor="let program of programList$ | async">
            {{ program.name }}
        </li>
    </ul>

CodePudding user response:

forkJoin operator could help here, you can do it like this:

this.form.controls.typeControl.valueChanges
  .pipe(
    switchMap((typeId: number) => {
     return forkJoin([
       this.typesService.getProjectsByType(typeId),
       this.typesService.getProgramsByType(typeId)
      ])
    })
  )
  .subscribe(([projects, programs]) => {
    //...
  });
  • Related