Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

1:N or N:1 relations of two objects in Angular2 as observables from a http request

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);
}
// ...
like image 636
Daniel Avatar asked Mar 31 '17 12:03

Daniel


1 Answers

the method getHeroesWithSuit() does two tasks.

  1. get the list of heroes.
  2. get suite details for each hero.

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.

like image 91
HirenParekh Avatar answered Oct 27 '22 01:10

HirenParekh