Referring to the "Tour of heroes" from the angular tutorial.
Assumed that we'll give all existing heroes the possibility to wear a special suite, but any other hero can wear the same suite too. We really don't want them to be naked. In this case we would have a related data object that holds all information’s about the available suits. The hero itself has to know which suite he is wearing and where he can find his selected one. For this case we would create a property that holds the ID of his chosen dress.
What would be the right way to resolve the relation between the hero and his suite in Angular as observable from http request?
E.g:
app/hero.ts
export class Hero {
id: number;
name: string;
suiteId: number;
}
app/suit.ts
export class Suite {
id: number;
name: string;
material: string;
color: string;
}
app/in-memory-data.service.ts
import { InMemoryDbService } from 'angular-in-memory-web-api';
export class InMemoryDataService implements InMemoryDbService {
createDb() {
let heroes = [
{id: 11, name: 'Mr. Nice', suitId: 11},
{id: 12, name: 'Narco', suitId: 12}
];
let suits= [
{id: 11, name: 'Nice and blue', material: 'Cotton', color: 'blue'},
{id: 12, name: 'Sexy and red', material: 'Silk', color: 'red'}
];
return {heroes, suits};
}
}
I know that something like next code block will work, but I would like to know how to resolve this the “observable” way.
// ...
getHeroesWithSuit(): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map( response => response.json().data as Hero[])
.do( response => response.forEach( ( data ) => data.suite = this.suiteService
.getSuit(data.suiteId)
.subscribe(suite => data.suite = suite as Suite)
))
.catch(this.handleError);
}
// ...
the method getHeroesWithSuit()
does two tasks.
And as we want to combine this two operations we need to use flatMap
and forkJoin
operators. Using these two operators getHeroesWithSuit()
function can be written in Observable way like this.
getHeroesWithSuit(): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(response => response.json().data as Hero[])
.flatMap((heroes: Heroes[]) => Observable.forkJoin(heroes.map((hero: Hero) => {
return this.suiteService.getSuit(hero.suiteId)
.map((suite: Suite) => {
hero.suite = suite;
return hero;
});
}))
);
}
flatMap
Operator lets you chain two Observables, returning a new Observable. here we are chaining the result of retrieve heroes service to the forkJoined observable.
And then we have mapped each hero to the getSuite()
method of the suiteServices
and passed the result to the forkJoin
operator.
the above code snippet represent the N:1 relation where each hero has one suite.
but what if each hero has multiple suites and suiteId
field of Hero
class is an Array.In that case we have to use forkJoin
operator one more time to retrieve info of all suites for each hero.
In this forkJoin
we will map all the suiteId
of each hero to the getSuit()
method of suiteServices
. And this will be the 1:N relation between objects.The update getHeroesWithSuit()
function will look like this.
getHeroesWithSuit():Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(response => response.json().data as Hero[])
.flatMap(
(heroes:Heroes[]) => Observable.forkJoin(heroes.map(
(hero:Hero) => {
return Observable.forkJoin(hero.suiteIds.map(
suiteId => {
return this.suiteService.getSuit(suiteId)
}
)).map(
suites => {
hero.suites = suites;
return hero;
})
}
))
);
}
here is the reference link it has good explanation of operators and how to use them to combine multiple Observable. I hope this will help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With