0

I have one service that returns a list of vehicles. I need to get the driver for each vehicle and set it for the bus. So far, I have implemented it in such a way:

    return this.getVehicles().pipe(
      // Set the driver
      map((res) => {
        res.items = res.items.map((vehicle) => {
          if (vehicle.driver_id !== "") {
            this.driverSvc.get(vehicle.driver_id).subscribe((data) => {
              vehicle.driver = data;
            })
          }
          return vehicle;
        })
        return res;
      }),
      map((res) => {
        // This map needs to have the driver!
        console.log(vehicle.driver) // Undefined in all cases, where it should be set for one of the entries
      })

I know the console.log(vehicle.driver) logs undefined because of the subscribe. Is there any other way to modify my response based on the response from another service?

1
  • you need to use the switchMap operator to combine two observables to a observable chain. Commented Dec 4, 2022 at 12:55

1 Answer 1

2

As i already mentioned you need to use the switchMap operator to combine the two observables to a chain. The following example could maybe work for you:

        return this.getVehicles.pipe(
            // load the driver
            switchMap((response) =>
                zip(
                    ...response.map((vehicle) =>
                        this.driverSvc.get(vehicle.driver_id).pipe(map((driver) => {
                            vehicle.driver = driver;
                            return vehicle;
                        })),
                    ),
                ),
            ),
            map((responseWithDriver) => {
                // this map needs to have the driver!
                console.log(responseWithDriver);
            }),
        );

The switchMap let us use a new observable as return value and the zip operator let us combine multiple calls to one return value.

UPDATE (with if)
As you ask in the comments here is a version with the if statement:

        return this.getVehicles.pipe(
            // load the driver
            switchMap((response) =>
                zip(
                    ...response.map((vehicle) => {
                        if (vehicle.driver_id === "") { // if no driver
                            return of(vehicle); // return observable from unedit data
                        }
                        // else return request with modified data
                        return this.driverSvc.get(vehicle.driver_id).pipe(map((driver) => {
                            vehicle.driver = driver;
                            return vehicle;
                        })),
                    }
                        
                    ),
                ),
            ),
            map((responseWithDriver) => {
                // this map needs to have the driver!
                console.log(responseWithDriver);
            }),
        );

The of operator is needed in this case because it generates a observable from our static vehicle and i think that zip just accepts observables.

Sign up to request clarification or add additional context in comments.

8 Comments

I just wonder if it would make a difference if you used forkJoin() instead of zip()?
Btw: I think you forgot the map-operator in the following part of your code: get(vehicle.driver_id).pipe((driver). And maybe the map at the end of your code should rather be a tap?
yea you are right, i muss the map in the driver get pipe. with forkJoin i am not shure if this would be the same because i think that forkjoin only emits if the observables completes. but you can try and look at the results
Assuming that this.driverSvc.get() merely triggers a http-request, I guess that forkJoin would be equivalent for our use case. Http-requests complete after their first emission and if one of the requests fails to complete, neither zip nor forkJoin would produce any output. As far as I understand zip only completes if all observables returned at least one value + one observable was completed.
My last question: ...response.map((vehicle) => means that you clone the resulting observable-array. I don't yet understand why this is necessary here. Anyway, I now gave you an upvote because your solution is basically the way to go ;-).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.