在订阅之前,如何遍历http响应和另一个请求,扩展初始响应?

时间:2019-10-24 10:42:38

标签: angular rxjs

我正在使用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);
        })
    );
}

2 个答案:

答案 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。