網站搜尋

rest - 以可觀察對象表示的兩個物件在 Angular2 中的 1:N 或 N:1 關係,來自 HTTP 請求


參考 Angular 教程中的「英雄之旅」。

假設我們會給所有現有的英雄提供穿特殊套裝的可能性,但任何其他英雄也可以穿同樣的套裝。我們真的不希望他們赤裸。在這種情況下,我們會有一個相關的資料物件來保存所有可用套裝的資訊。英雄本身必須知道他正在穿哪套套裝,以及在哪裡可以找到他選擇的套裝。針對這種情況,我們會建立一個屬性來保存他所選服裝的 ID。

在 Angular 中,從 HTTP 請求作為可觀察對象來處理英雄與其套件之間的關係,正確的方法應該是什麼?

例如:

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};
  }
}

我知道像下一個程式碼區塊的東西會奏效,但我想知道如何用「observable」的方式來解決這個問題。

// ...
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);
}
// ...

答案#1

方法 getHeroesWithSuit() 執行兩個任務。

  1. 取得英雄列表。
  2. 取得每個英雄的套裝詳情。

由於我們想要結合這兩個操作,我們需要使用 flatMap 和 forkJoin 運算子。使用這兩個運算子,getHeroesWithSuit() 函數可以以 Observable 的方式這樣編寫。

    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 運算符讓你可以鏈接兩個 Observables,返回一個新的 Observable。在這裡,我們將 retrieve heroes 服務的結果鏈接到 forkJoined observable。

然後我們將每個英雄映射到 suiteServices 的 getSuite() 方法,並將結果傳遞給 forkJoin 運算子。

上述程式碼片段表示 N:1 的關係,其中每個英雄都有一個套裝。

但是如果每個英雄有多個套件,並且 Hero 類的 suiteId 欄位是一個陣列。在這種情況下,我們必須再使用一次 forkJoin 運算子,以檢索每個英雄所有套件的資訊。

在這個 forkJoin 中,我們將把每個英雄的所有 suiteId 映射到 suiteServices 的 getSuit() 方法。這將是物件之間的 1:N 關係。更新後的 getHeroesWithSuit() 函數將如下所示。

  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;
              })
          }
        ))
      );
  }

這裡是參考連結,它對操作符以及如何使用它們來組合多個 Observable 有很好的說明。我希望這會有所幫助。