There are situations where we make an asynchronous request to get a list of entities (albums in the example). For each album we need to send another asynchronous request to get more detail about the entity, so that we can transofrm the entity with more detailed data. From the design point of view the cleanes solution is to hold all the logic needed for that process in a service, and the controller will ideally subscribe to a single observer that delivers the enriched entity (the original entity+ further details for the second data request).
We have two levels: the outer level gets the list of albums and tranform the list of albums in another Observable. For each emission of this Observable we are going to zip (to package) the album as observable (with a unique emission: the album), and another observable that makes the detailed information request. In the transformation of this zipped Observable detailed data is added to the information retrieved for the album.
getAlbums(): Observable<Album> {
return this._http.get(this.urlRestfulApi + '/album')
// flats the array of one observable to a single observable
.flatMap(albumList =>
Observable.from(albumList.json().albums)
// flats the array of one observable for each album in an array
// of a single observable
.flatMap(response => {
let album = response as Album
// Combine one observable for each album with the observable to
// get the portrait of the album
return Observable.zip (
Observable.of(album),
this.getFirstImage(album._id),
(album, uri) => {
// Tranforms the album object adding the uri of the portrait,
// extracted for the second observable
album.portraitUrl = uri
return album
}
)
})
)
}
getFirstImage(idAlbum: string) : Observable<string> {
// If we use map we get an array of Observables with only one
// observable, while what we want is a unique observable with
// the emission of one object (the firt photo)
return this._http.get(this.urlRestfulApi + `/image/album/${ idAlbum }`)
.flatMap(imagesList => {
return Observable.from(imagesList.json().images)
// get the first image associated with the album, we ignore rest fo images
.take(1)
.map(result => {
let image = result as Image
// Return the uri instead the whole image
return this.urlRestfulApi + `/image-file/${ image._id }`
})
})
}