我正在使用Angular 6,我需要扩展从Http请求获得的一系列联系人。每个联系人都有一个userId。我需要遍历contacts数组,根据userId发出另一个请求,以便可以使用用户的名和姓扩展每个联系人。我不得不提一下,在订阅之前必须先这样做。
contacts.service.ts
findContacts(params: paramsModel): Observable<resultsModel> {
const httpParams = this.getParams(params);
return this.http.get<resultsModel>('/getAllContacts', {
params: httpParams
}).pipe(
map((res: any) => {
// here I get all the contacts but I need to loop through each contact and make another request based on the userId to get info about the user that belongs to each contact
// res looks like this:
// {
// [
// id: 1,
// name: test1,
// userId: 14
// ],
// [
// id: 2,
// name: test2,
// userId: 9
// ],
// ..
// }
// and I need to extend it to like this:
// {
// [
// id: 1,
// name: test1,
// userId: 14,
// userFirstName: John,
// userLastName: Doe,
// ],
// [
// id: 2,
// name: test2,
// userId: 9,
// userFirstName: Jane,
// userLastName: Doe,
// ],
// ..
// }
// I tried a lot of approaches, but with no luck.. (including flatMap, switchMap, working with chaining observables, but I didn't find an example with looping before subscribe that I could reproduce so I got lost quickly).
// this is one of them:
// res.map(contact => {
// this.http.get<any>('getUserById?userId=' + contact.userId).pipe(
// map( user => {
// // using underscore for extend
// _.extend(contact, {
// userFirstName: user.firstName,
// userLastName: user.lastName
// });
// return contact;
// })
// );
// });
return someFunction(res, params);
})
);
}
contacts.data.ts
export class ContactsData extends Data {
constructor(private contactsService: ContactsService) {
super();
}
loadContacts(
params: paramsModel
) {
this.loadingSubject.next(true);
this.contactsService.findContacts(params).pipe(
tap(res => {
this.entitySubject.next(res.items);
this.paginatorTotalSubject.next(res.totalCount);
}),
catchError(err => of(new resultsModel([], err))),
finalize(() => this.loadingSubject.next(false))
).subscribe();
}
}
contacts-list.component.ts
export class ContactsListComponent implements OnInit {
contactsData: ContactsData;
ngOnInit() {
const params = new paramsModel();
this.contactsData.loadContacts(params);
}
}
解决方案,在MichałTkaczyk的帮助下:
contacts.service.ts
findContacts(params: paramsModel): Observable<resultsModel> {
const httpParams = this.getParams(params);
return this.http.get<resultsModel>('/getAllContacts', {
params: httpParams
}).pipe(
map((res: any) => res),
switchMap(res => {
const requests = res.map(contact => {
return this.http.get<any>('getUserById?userId=' + contact.userId).pipe(
map(userData => {
console.log(userData);
return _.extend(contact, {
userFirstName: user.firstName,
userLastName: user.lastName
});
})
);
});
return forkJoin(requests);
}),
map( res => {
return someFunction(res, params);
})
);
}
答案 0 :(得分:1)
首先将rxjs映射更改为switchMap,然后可以迭代来自第一个Observable的数据,并创建一个可观察变量数组,这些数组可以在forkMap中作为forkJoin返回。
代码:
switchMap(res => {
const requests = res.map(contact => {
return this.http.get<any>('getUserById?userId=' + contact.userId);
});
return forkJoin(requests);
}, (resData, usersData) => [resData, usersData]),
然后,您将获得数组中的所有数据(resData是在switchMap之前获得的数据,usersData是从forkJoin获得的数据,您应该先创建地图,然后为每个用户查找数据
map(([res, usersRes]) => {
return res.map(contact => {
const foundUser = usersRes.find(user => user.id === res.id);
return {
...res,
...foundUser
};
});
})
您可以尝试使用此代码,尽管我不建议创建太多请求,但是更好的方法是从API调用中获取此数据。
编辑:
正如Oles Savluk所述,switchMap中的resultSelector已过时,因此您可以在forkJoin中的每个可观察到的地方添加另一个管道:
switchMap(res => {
const requests = res.map(contact => {
return this.http.get<any>('getUserById?userId=' + contact.userId).pipe(
map(userData => {
return { ...contact, ...userData };
})
);
});
return forkJoin(requests);
})
答案 1 :(得分:0)
获取所有联系人->将联系人映射到可观察到的联系人->发出每个项目->使用每个项目形成一个http请求->在该http请求上通过地图获取地图,以利用联系人获取格式结果和用户详细信息->使用toArray()将其收集为数组->订阅。返回所需输出的数组。
this.http.get<resultsModel>('/getAllContacts', { params: httpParams}).pipe(
mergeMap(contacts=> from(contacts)),
concatMap((contact) => this.http.get<any>('getUserById?userId=' + contact.userId).pipe(
map((userDetails) => {
contact['userFirstName']=userDetails['userFirstName'];
contact['userLastName']=userDetails['userLastName'];
return contact
}))),
toArray()).subscribe(res => console.log(res));
PS-如果顺序无关紧要,请使用mergeMap代替concatMap。