试图绕过Observables并链接/嵌套HTTP请求。
假设我的Dog-Walking API后端具有以下REST端点,这些端点无法更改:
GET /dogs
(返回所有狗):
[
{ id: 1, name: 'Fido' },
{ id: 2, name: 'Barky' },
{ id: 3, name: 'Chip' },
{ id: 4, name: 'Bracken' }
]
GET /walker/:id
(返回一个dog狗犬):
{ id: 1, name: 'John Doe' }
GET /pairings
(返回狗和步行者之间的所有配对):
[
{ id: 1, dogIds: [2], walkerId: 1 },
{ id: 2, dogIds: [1, 3], walkerId: 2 }
]
我想提供一个步行者和狗之间所有配对的列表,按步行者名称排序。我想按名称对每个步行者的狗进行分类。我不想显示没有主动配对的任何步行者或狗,例如:
Walker | Dogs
-------------+-----------
John Doe | Barky
Jan Kowalksi | Chip, Fido
/pairings
和/dogs
pairing
,然后填充dogs
字段walkerId
中抽出pairing
,并并行请求每个/walker/:id
pairing
,然后填充walker
字段我觉得我可以使用Promises轻松地做到这一点,但是我正在努力使自己的大脑适应在Observables中的思考。这是到目前为止(使用Angular的HttpClient
):
function getDogWalkerPairings() {
return Observable.forkJoin([
this.http.get('/pairings'),
this.http.get('/dogs')
])
.map(
(res) => {
const pairings = res[0];
const dogs = res[1];
return pairings.map(p => {
const pDogs = p.dogIds.map(dogId =>
dogs.find(d => (d.id === dogId)
);
return Object.assign({ dogs: pDogs }, p);
});
}
)
.map((pairingsWithDogs) => {
return Observable.forkJoin(
pairingsWithDogs.map(p => this.http.get('/walkers/' + p.walkerId))
);
})
.map((walkers) => {
// uhhh... where to now?
// I don't have a reference to pairings in this scope :/
});
}
答案 0 :(得分:1)
好的,我尝试一下:-)
我的方法是尽可能地提取到函数中。对我来说,这有助于获得更好的画面。 我将其从“ .map()”更改为“ pipe(map())”,这是v5.5以来的新RxJs样式。
function getDogWalkerPairings() {
return Observable.forkJoin([
this.http.get('/pairings'),
this.http.get('/dogs')
]).pipe(
map([pairings, dogs] => createPairingsWithDogs(pairings, dogs) ),
switchMap( pairingsWithDogs => getWalkersForPairs(pairingsWithDogs) )
)
}
function createPairingsWithDogs(pairing, dogs){
return pairings.map(pairing => {
const dogPairings = pairing.dogIds.map(
dogId => dogs.find( dog => dog.id === dogId)
);
return Object.assign( {dogs: dogPairings }, pairing )
}
}
function getWalkersForPairs(pairingsWithDogs):Observable<any>{
return Observable.forkJoin(
pairingsWithDogs.map(p => this.http.get('/walkers/' + p.walkerId))
).pipe(
map( walkerArray => createWalkerDogPairs(walkerArray, pairingsWithDogs) )
);
}
function createWalkerDogPairs(walkerArray, pairingsWithDogs){
...
return finalResultTable;
}
它如何工作? 首先,我像您一样创建配对。
然后更改流(switchMap)。在这里,我对forkJoin使用技巧。但是,当我将其提取到它自己的函数中时,我会创建一个新的作用域...并且这里有我需要的一切。 (好吧,那里没有cookie,所以不是所有的东西... :-()
如果这是我的编码,我还会添加很多类型。特别是当我切换类型(带有“地图”)时,这可以帮助我保持领先地位
Observable.of( [1,2,3,4] ).pipe(
map( (numbers: number[]): boolean[] => checkOddNumbers(numbers) ),
tap( (data: boolean[] => console.log(data) )
)
我希望能有所帮助。
热烈的问候
PS:我知道,我的方法名很棒...:-(
答案 1 :(得分:1)
使用concatMap
链接最终的http呼叫。棘手的部分是您需要传回您为第一个forkJoin
获得的配对。这是我的答案,以及有效的example on stackblitz。我嘲笑了您的http调用,延迟了500ms,只是在使用它们的地方简单地使用了适当的http调用。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable, of, forkJoin, merge, Subject } from 'rxjs';
import { map, delay, concatMap, takeUntil } from 'rxjs/operators';
interface IdNamePair {
id: number;
name: string;
}
interface Pairing {
id: number;
dogIds: Array<number>;
walkerId: number;
dogs?: Array<IdNamePair>;
walker?: IdNamePair;
}
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
pairings: Array<Pairing>;
private delay = 500;
private ngUnsubscribe: Subject<any> = new Subject();
constructor() { }
ngOnInit() {
this.getDogWalkerPairings();
}
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
private getDogWalkerPairings() {
forkJoin(this.getPairings(), this.getDogs())
.pipe(
map(this.mapToPairingsWithDogs),
concatMap((pairingsWithDogs: Array<Pairing>) => {
return forkJoin(pairingsWithDogs.map(pair => {
return forkJoin(this.getWalker(pair.walkerId), of(pair));
}));
}),
map(this.mapToPairingsWithDogsAndWalker),
takeUntil(this.ngUnsubscribe)
)
.subscribe((pairings: Array<Pairing>) => {
console.log(pairings);
this.pairings = pairings;
});
}
private mapToPairingsWithDogs(data: [Array<Pairing>, Array<IdNamePair>]): Array<Pairing> {
const pairings = data[0];
const dogs = data[1];
return pairings.map(pairing => {
const pDogs = pairing.dogIds.map(dogId => dogs.find(d => (d.id === dogId)));
pairing.dogs = pDogs;
return pairing;
});
}
private mapToPairingsWithDogsAndWalker(data: Array<[IdNamePair, Pairing]>): Array<Pairing> {
return data.map(d => {
const pairing: Pairing = d[1];
pairing.walker = d[0];
return pairing;
});
}
private getDogs(): Observable<Array<IdNamePair>> {
return of([
{ id: 1, name: 'Fido' },
{ id: 2, name: 'Barky' },
{ id: 3, name: 'Chip' },
{ id: 4, name: 'Bracken' }
]).pipe(delay(this.delay));
}
private getWalker(id: number): Observable<IdNamePair> {
return of({ id: id, name: id === 1 ? 'John Doe' : 'Jane Doe'}).pipe(delay(this.delay));
}
private getPairings(): Observable<Array<Pairing>> {
return of([
{ id: 1, dogIds: [2], walkerId: 1 },
{ id: 2, dogIds: [1, 3], walkerId: 2 }
]).pipe(delay(this.delay));
}
}
编辑
说明:
forkJoin
-将同时返回配对和狗 map
-将狗与配对配对,我们在其中找到它们的ID concatMap
-将执行下一个调用,为此,我们需要做一些事情
getWalker
,我们需要同时获取所有结果,因此我们将每个 pairing 映射到一个getWalker
方法调用返回一个Observable
,最后我们forkJoin
映射了Observable
数组concatMap
范围内可用的配对传递给下一个map
或subscribe
,我们需要forkJoin
中每个Observable
的{{1}},其中每个getWalker
都是根据单个配对创建的Observable
。map
-将 walker 与 pairing 配对,我们在其中找到他们的ID