Calling another observable with the response of another in Angular 4
Asked Answered
H

2

10

I am writing an Angular 4 application using HttpClient that displays movie show times. There are 2 JSON files where the data is located: showtimes.json and movies.json.

// showtimes.json    
[{
"id": "2030c64ce72b4e4605cb01f2ba405b7d",
"name": "Arclight", // need to display this information
"showtimes": {
  "b4c2c326a4d335da654d4fd944bf88d0": [ // need to use this id
      "11:30 pm", "2:45 pm", "8:35 pm", "4:15 pm", "10:30 pm"
  ]
 } 
}]

// movies.json
[{
"b4c2c326a4d335da654d4fd944bf88d0": { // to retrieve the title, rating, and poster
  "title": "Fifty Shades Darker", // needs to be displayed
  "rating": "R", // needs to be displayed
  "poster": "https://dl.dropboxusercontent.com/s/dt6wgt92cu9wqcr/fifty_shades_darker.jpg" // needs to be displayed
}
}]

I have service that can retrieve the title and name of the theater. But now I must use the value in the showtimes object to display the correct title name. As you see b4c2c326a4d335da654d4fd944bf88d0 is the id of the movie title and can be retrieved from the movies.json file.

So far this is my component

ngOnInit() {
  this._moviesDataService.getShowtimes()
  .subscribe(res => this.results = res)
}

And this is my service.

getShowtimes (): Observable<ShowTimes> {
  return this._http.get<ShowTimes>(this._showtimesURL)
}

My question is how do I retrieve the title of the movie using its id? Would this require two chained Observables? Would I need to loop through the movies array and .filter it?

I've included an example of what I am trying to build Example

Hazaki answered 23/2, 2018 at 23:40 Comment(1)
Yes, you will need to combine observables. Look into e.g. forkJoin and the various map methods.Samarium
D
16

Usually, when you have one Observable and you need to grab something from . it and return a different Observable, you can use switchMap:

ngOnInit() {
    this._moviesDataService.getShowtimes()
        .switchMap(res => { 
            const id = Object.keys(res[0].showtimes)[0]; // assuming you have one element in your array and you want the first id from showtimes
            return this.getMovies(id); // assuming, you have a separate method that returns the movies
        })
        .subscribe(res => this.results = res)
}

UPDATE

Since you need results of both Observable but also you need the results of the first to request the second here's an idea how you can do this:

ngOnInit() {
    this._moviesDataService.getShowtimes()
        .switchMap(res => { 
            const showtimes = res[0].showtimes;
            const id = Object.keys(showtimes)[0];

            return Observable.zip(
                this.getMovies(id),
                Observable.of(showtimes[id])
            );
        })
        .subscribe(([movies, showtimes]) => {
            this.results.movies = movies; // or assign it to some other property
            this.results.showtimes = showtimes; // and use in the template
        }
Dachi answered 23/2, 2018 at 23:50 Comment(5)
No, forkJoin takes several requests and returns the responses as an array, you can&#39;t send one request based on the data received from another, you need a switchMap (or a mergeMap)Dachi
I actually need to display the data from getShowtimes() and then pass in the showtimes object to retrieve the titles from getMovies(). When I iterate through this.results, only the data from getMovies() is there. Is there a way to send the data from the first call to the front end and also perform another Observable with switchMap()?Hazaki
can you update your post with the desired output (what you want this.results to equal to)? it's hard to understand what exactly you need to do.Dachi
Awesome! Got both responses that I need!Hazaki
Great! Happy to help!Dachi
E
1

I think because you need to retrieve titles of all the movies, you have to chain the array of IDs in the first request's response, into a series of requests for movies' titles. something like this: (assuming you have a method like getMovieTitle that gets data of a movie based on it's id and it returns an observable)

this._moviesDataService.getShowtimes()
        .switchMap(res => { 
            let resArray: any[] = res.map(
                    item=>this._moviesDataService.getMovieTitle(
                        Object.keys(item.showtimes)[0]
            ))
            return Observable.merge(...resArray);
        })
        .subscribe(res => /*you will get a new res for each movie title here*/)

what Observable.merge does, is it Turn multiple observables into a single observable. so you will get all the results in one subscription.

Note: don't forget to assign all of this to a subscription and unsubscribe it at ngOnDestroy of the component (to prevent memory leak)

Eggers answered 24/2, 2018 at 7:45 Comment(1)
I actually need to display the data from getShowtimes() and then pass in the showtimes object to retrieve the titles from getMovies(). When I iterate through this.results, only the data from getMovies() is there. Is there a way to send the data from the first call to the front end and also perform another Observable with switchMap()?Hazaki

© 2022 - 2024 — McMap. All rights reserved.