2
votes

I have a POST request to create a Movie that returns an Observable, the result of this request returns me an ID that I need to make two further requests using this ID to add a Director & Movie Images.

I have the following call, it works perfectly when it comes to adding the Director however when I get to the second flatMap I'm unable to loop through the images to POST them without adding a subscribe on the end.

Is there a better way to do this? I have tried switchMap, mergeMap and map but I cannot get the second request to fire without the subscribe.

this.Movie.createMovie(movie).pipe(
      map((movie: Movie) => {
        return movie;
      }),
      switchMap((movie: Movie) => this.movieRepository.postDirector(director, movie.id)),
      flatMap((director: Director) => {
        return movieImages.map((image) => {
          return this.movieRepository.addMovieImage(image, director.movie_id).subscribe()
        });
      })
    ).subscribe({
      next: (response: any) => {
        console.log(response)
      }
    })
3
flatMap resolves an observable returned from the callback, but your callback returns an array. You need e.g. forkJoin to turn an array of observables into an observable of an array.jonrsharpe
also if you have more that one "subscribe" in your rxjs code - you are surely doing something wrongAndrei
@Andrei not a very helpful comment.Stacker-flow

3 Answers

1
votes

Assuming you want to save director and the image and both calls must be finished before I save another movie, I would have approached it this way

 this.Movie.createMovie(movie).pipe(
    concatMap((movie: Movie) => {
      const saveDirectory$ = this.movieRepository.postDirector(director, movie.id);
      const saveMovieImage$ = this.movieRepository.addMovieImage(image, director.movie_id);

     return forkJoin(saveDirectory$, saveMovieImage$).pipe(map(([directoryAPIresponse, imageApiResponse])=>{
       // you can check if the both records created based on your api response;
       return of(true);
     }));
    )).subscribe({
      next: (response: any) => {
        console.log(response)
      }
    })

The reason I use concatMap because I want to wait for inner apis to complete before I make another call.

forkJoin: I want both inner APIs to complete.

0
votes

Convert your array to an array of observable and run them in forkJoin

flatMap((director: Director) => {
    return forkJoin(movieImages.map((image) => {
      return this.movieRepository.addMovieImage(image, director.movie_id)
    }));
})
0
votes

Great question;

Maybe what you need is to properly orchestrate the operations, for which you can use Tuples(pair) and a more ordered code like this:

this.Movie.createMovie(movie)
  .pipe(
    concatMap(movie => this.movieRepository.postDirector(director, movie.id).pipe(map(director => [movie, director]))),
    concatMap(pair => this.movieRepository.addMovieImage((pair[0] as Movie).image, (pair[1] as Director).movie_id))
  )
  .subscribe(response => console.log(response));

You can also use switchMap instead of a concatMap. If you need more information on how to orchestrate operations, I recommend the following article: Clean the operators's chain in RxJS.

Greetings, I hope I have helped.