使用 rxjs Observable<Array<T>,我如何为每个数组项调用 http 服务并将该 observable 的结果分配给 属性?
With rxjs Observable<Array<T>, how can I make an http service call for each array item and assign the result of that observable to a property?
基本上,我正在尝试从 2 个端点收集比赛参与者的数据。我的服务中有 2 个方法使用 angular http-client 和 return Observables 发出 api 请求。第一种方法采用一个 ID 数组,并为每个传递的 ID 取回基本参与者数据(姓名、经过时间、当前检查点)的数组,第二种方法采用单个 Id 和 returns 数组该参与者每个检查点的结果
我有一个 Observable,它是第一次服务调用的结果。然后我通过管道和映射它,以便我可以使用数组,然后做一个标准的 javascript 数组映射来迭代元素。我真正坚持的一点是数组中每个 'Participant' 匹配条件,我需要对第二个端点进行进一步的 http 调用并将结果可观察值的值分配给 属性 的参与者。理想情况下,原始 Observable 会在可用时立即 return,然后当 api 调用完成时,结果数组中的每个参与者都会更新检查点结果。
我才刚刚开始学习 rxjs、angular 和 ngrx,我仍然在 Observable 流方面苦苦挣扎,但是我得到了一个看起来像这样的 ngrx 效果
@Effect()
pollDataForCollection$ = this.collectionEventsActions$.pipe(
ofType(CollectionActions.onCountdownRestarted.type),
withLatestFrom(
this.featuresStore$.select(collectionSelectors.selectAllFavourites),
this.featuresStore$.select(collectionSelectors.selectExpandedFavourites)
),
switchMap(([action, faves, expandedDetailIds]) => {
const ids: number[] = faves.map(fave => fave.id);
return this.apiService.getParticipantsById(ids).pipe(
map((participants: Participants[]) => {
return participants.map(participant => {
if (expandedDetailIds.includes(participant.id)) {
this.apiService.getParticipantDetailResults(participant.id).pipe(
map((results: ParticipantResults[]) => {
return (participant.results = results);
})
);
}
return participant;
});
}),
map(updates => {
debugger;
return CollectionActions.pollingDataSucceeded({ items: updates });
}),
catchError(error => of(CollectionActions.pollingDataErrored({ error })))
);
})
);
这成功地获得了参与者数组和每个参与者的项目,但不出所料,没有将结果分配给参与者。
更新以包含工作解决方案
非常感谢@user2216584 的解决方案
@Effect()
pollDataForCollection$ = this.collectionEventsActions$.pipe(
ofType(
CollectionActions.onCountdownRestarted.type
)
, withLatestFrom(
this.featuresStore$.select(collectionSelectors.selectAllFavourites)
)
, switchMap(([action, faves]) => {
const ids: number[] = faves.map(fave => fave.id);
return this.apiService.getParticipantsById(ids);
})
, withLatestFrom(
this.featuresStore$.select(collectionSelectors.selectExpandedFavourites)
)
, switchMap(([participants, expandedFaves]) => {
const favesWithDetailedResults = participants.map(participant => {
if(expandedFaves.includes(participant.id)){
return this.apiService.getParticipantCheckpointResults(participant.id).pipe(
map((checkpointResults: ParticipantCheckpoint[]) => {
const participantDetail: Participant = {
...participant,
checkpoints: checkpointResults
}
return participantDetail;
})
)
}else{
return of(participant)
}
});
return forkJoin(favesWithDetailedResults)
})
, map((updates) => {
return CollectionActions.pollingDataSucceeded({ items: updates })
})
, catchError(error =>
of(CollectionActions.pollingDataErrored({ error }))
)
);
这就是你需要做的 -
对于每个参与者,您需要从详细信息 api 中收集可观察对象并将其推送到数组。该数组应该是可观察对象的集合。然后在rxjs
的forJoin
函数中使用这个数组。看下面代码
-
@Effect()
pollDataForCollection$ = this.collectionEventsActions$.pipe(
ofType(
CollectionActions.onCountdownRestarted.type
)
, withLatestFrom(
this.featuresStore$.select(collectionSelectors.selectAllFavourites)
,this.featuresStore$.select(collectionSelectors.selectExpandedFavourites)
)
, switchMap(([action, faves, expandedDetailIds]) => {
const ids: number[] = faves.map(fave => fave.id);
return this.apiService.getParticipantsById(ids);
),
switchMap(participants => {
//for each participant map the detail api observable in an array
const obs = participants.map(p => this.apiService.getParticipantDetailResults(participant.id));
return forkJoin(obs);
}),
map(joined => {
// result from forkJoin will be an array of the api response in the order you added the observables. Also forkJoin only emit the value once for each of the observable it has the response; so it will wait for the response to comeback for the details for each participant
//now you can do whatever you want to do with this array
// may be parse it and send the success action to the store
})
基本上,我正在尝试从 2 个端点收集比赛参与者的数据。我的服务中有 2 个方法使用 angular http-client 和 return Observables 发出 api 请求。第一种方法采用一个 ID 数组,并为每个传递的 ID 取回基本参与者数据(姓名、经过时间、当前检查点)的数组,第二种方法采用单个 Id 和 returns 数组该参与者每个检查点的结果
我有一个 Observable,它是第一次服务调用的结果。然后我通过管道和映射它,以便我可以使用数组,然后做一个标准的 javascript 数组映射来迭代元素。我真正坚持的一点是数组中每个 'Participant' 匹配条件,我需要对第二个端点进行进一步的 http 调用并将结果可观察值的值分配给 属性 的参与者。理想情况下,原始 Observable 会在可用时立即 return,然后当 api 调用完成时,结果数组中的每个参与者都会更新检查点结果。
我才刚刚开始学习 rxjs、angular 和 ngrx,我仍然在 Observable 流方面苦苦挣扎,但是我得到了一个看起来像这样的 ngrx 效果
@Effect()
pollDataForCollection$ = this.collectionEventsActions$.pipe(
ofType(CollectionActions.onCountdownRestarted.type),
withLatestFrom(
this.featuresStore$.select(collectionSelectors.selectAllFavourites),
this.featuresStore$.select(collectionSelectors.selectExpandedFavourites)
),
switchMap(([action, faves, expandedDetailIds]) => {
const ids: number[] = faves.map(fave => fave.id);
return this.apiService.getParticipantsById(ids).pipe(
map((participants: Participants[]) => {
return participants.map(participant => {
if (expandedDetailIds.includes(participant.id)) {
this.apiService.getParticipantDetailResults(participant.id).pipe(
map((results: ParticipantResults[]) => {
return (participant.results = results);
})
);
}
return participant;
});
}),
map(updates => {
debugger;
return CollectionActions.pollingDataSucceeded({ items: updates });
}),
catchError(error => of(CollectionActions.pollingDataErrored({ error })))
);
})
);
这成功地获得了参与者数组和每个参与者的项目,但不出所料,没有将结果分配给参与者。
更新以包含工作解决方案
非常感谢@user2216584 的解决方案
@Effect()
pollDataForCollection$ = this.collectionEventsActions$.pipe(
ofType(
CollectionActions.onCountdownRestarted.type
)
, withLatestFrom(
this.featuresStore$.select(collectionSelectors.selectAllFavourites)
)
, switchMap(([action, faves]) => {
const ids: number[] = faves.map(fave => fave.id);
return this.apiService.getParticipantsById(ids);
})
, withLatestFrom(
this.featuresStore$.select(collectionSelectors.selectExpandedFavourites)
)
, switchMap(([participants, expandedFaves]) => {
const favesWithDetailedResults = participants.map(participant => {
if(expandedFaves.includes(participant.id)){
return this.apiService.getParticipantCheckpointResults(participant.id).pipe(
map((checkpointResults: ParticipantCheckpoint[]) => {
const participantDetail: Participant = {
...participant,
checkpoints: checkpointResults
}
return participantDetail;
})
)
}else{
return of(participant)
}
});
return forkJoin(favesWithDetailedResults)
})
, map((updates) => {
return CollectionActions.pollingDataSucceeded({ items: updates })
})
, catchError(error =>
of(CollectionActions.pollingDataErrored({ error }))
)
);
这就是你需要做的 -
对于每个参与者,您需要从详细信息 api 中收集可观察对象并将其推送到数组。该数组应该是可观察对象的集合。然后在rxjs
的forJoin
函数中使用这个数组。看下面代码
-
@Effect()
pollDataForCollection$ = this.collectionEventsActions$.pipe(
ofType(
CollectionActions.onCountdownRestarted.type
)
, withLatestFrom(
this.featuresStore$.select(collectionSelectors.selectAllFavourites)
,this.featuresStore$.select(collectionSelectors.selectExpandedFavourites)
)
, switchMap(([action, faves, expandedDetailIds]) => {
const ids: number[] = faves.map(fave => fave.id);
return this.apiService.getParticipantsById(ids);
),
switchMap(participants => {
//for each participant map the detail api observable in an array
const obs = participants.map(p => this.apiService.getParticipantDetailResults(participant.id));
return forkJoin(obs);
}),
map(joined => {
// result from forkJoin will be an array of the api response in the order you added the observables. Also forkJoin only emit the value once for each of the observable it has the response; so it will wait for the response to comeback for the details for each participant
//now you can do whatever you want to do with this array
// may be parse it and send the success action to the store
})