rest - 以可觀察對象表示的兩個物件在 Angular2 中的 1:N 或 N:1 關係,來自 HTTP 請求
參考 Angular 教程中的「英雄之旅」。
假設我們會給所有現有的英雄提供穿特殊套裝的可能性,但任何其他英雄也可以穿同樣的套裝。我們真的不希望他們赤裸。在這種情況下,我們會有一個相關的資料物件來保存所有可用套裝的資訊。英雄本身必須知道他正在穿哪套套裝,以及在哪裡可以找到他選擇的套裝。針對這種情況,我們會建立一個屬性來保存他所選服裝的 ID。
在 Angular 中,從 HTTP 請求作為可觀察對象來處理英雄與其套件之間的關係,正確的方法應該是什麼?
例如:
app/hero.tsexport class Hero {
id: number;
name: string;
suiteId: number;
}
app/suit.tsexport class Suite {
id: number;
name: string;
material: string;
color: string;
}
app/in-memory-data.service.tsimport { 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() 執行兩個任務。
- 取得英雄列表。
- 取得每個英雄的套裝詳情。
由於我們想要結合這兩個操作,我們需要使用 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 有很好的說明。我希望這會有所幫助。
