I am writing an effect that requires the results of several separate service calls (returning as observables) before completing it. The first service call must complete before 3 more (which can be async), and then I build my action list. I have it working, but it feels like it is needlessly nested.
@Effect() loadMyData$: Observable<any> = this.actions$.pipe(
ofType(fromMyData.LOAD_DATA),
switchMap((action: fromMyData.Load) => {
return this.dataService.openMyData(action.payload).pipe(
switchMap(() => this.dataService.getShapes()),
switchMap(shapeData => {
return this.dataService.getCircles().pipe(
switchMap((circleData) => {
return this.dataService.getTriangles().pipe(
switchMap((triangleData) => {
const flatMyDataWithReferences: any = {
triangleName: triangleData.name,
shapeName: shapeData.name
};
return [
new fromShapes.Load(),
new fromBalls.Load(),
new fromCircles.LoadListSuccess(
this.organizeCircles(circleData)),
new fromMyData.LoadSuccess(flatMyDataWithReferences),
new fromUi.AddMessage({
type: MessageType.Success, message: 'Successfully loaded my data ' +
shapeData.name +
' from: ' + action.payload
})
];
}),
catchError((err) => this.myDataLoadErrorEvents(err))
);
}),
catchError((err) => this.myDataLoadErrorEvents(err))
);
}),
catchError((err) => this.myDataLoadErrorEvents(err))
);
}),
catchError((err) => this.myDataLoadErrorEvents(err))
);
Basically, in my example code here, once I have called and returned from the dataService.openMyData
call, I would like to do these calls in parallel:
dataService.getShapes
dataService.getCircles
dataService.getTriangles
Once they all complete, I would like use their returned data within my array of actions to put in the return [new etc...]
to wrap up the effect.
hoping someone has a more elegant way to approach the 3 intermediary service calls than this ugly (and needless) indentation hell...
I looked around and found that some people are using forkJoin to wait for the result of multiple observables, but it appears they can't use the result of that forkJoin observable as a return on which to operate. For example, this code below tells me that I am not creating an observable in the end, which is what my effect requires..
@Effect() loadMyData$: Observable<any> = this.actions$.pipe(
ofType(fromMyData.LOAD_DATA),
switchMap((action: fromMyData.Load) => {
return this.dataService.openMyData(action.payload).pipe(
switchMap(() => {
return forkJoin(
this.dataService.getShapes(),
this.dataService.getCircles(),
this.dataService.getTriangles()
).pipe(
map(joinResult => {
const [shapeData, circleData, triangleData] = joinResult;
const flatMyDataWithReferences: any = {
triangleName: triangleData.name,
shapeName: shapeData.name
};
return [
new fromShapes.Load(),
new fromBalls.Load(),
new fromCircles.LoadListSuccess(
this.organizeCircles(circleData)),
new fromMyData.LoadSuccess(flatMyDataWithReferences),
new fromUi.AddMessage({
type: MessageType.Success, message: 'Successfully loaded my data ' +
shapeData.name +
' from: ' + action.payload
})
];
})
);
}),
catchError((err) => this.myDataLoadErrorEvents(err))
);
}),
catchError((err) => this.myDataLoadErrorEvents(err))
);