I have the following effect:
loadOrganisations$ = createEffect(() => this.actions$.pipe(
ofType(AdminOrganisationListActions.loadOrganisations),
switchMap(() => combineLatest(this.adminOrganisationService.list(), this.adminProductService.listProducts()).pipe(
map(([organisations, products]) => AdminOrganisationListActions.loadOrganisationsSuccess({ organisations, products })),
catchAppError(error => of(AdminOrganisationListActions.loadOrganisationsFailure({ error })))
))
));
It basically calls 3 end points and passes the results to an action.
I need to modify this effect to allow me to call another service with the results of this.adminOrganisationService.list().
I have tried to create a new map, to enable me to iterate over organizations and call the service for each organization, but its not working correctly.
loadOrganisations$ = createEffect(() => this.actions$.pipe(
ofType(AdminOrganisationListActions.loadOrganisations),
switchMap(() => combineLatest([
this.adminOrganisationService.list(),
this.adminProductService.listProducts(),
this.adminProductUpgradeService.listProductUpgrades()]).pipe(
map(([organisations, products, upgrades]) => {
let features = new Map<number, any>();
organisations.forEach(o => {
// Call Service here for each organisation and assign result to features
this.adminService.getOrganisationAppFeatures(o.id).pipe(map(feature => features.set(o.id, feature))).subscribe();
})
return [organisations, products, upgrades, of(features)];
}),
map(([organisations, products, upgrades, features]) =>
AdminOrganisationListActions.loadOrganisationsSuccess({ organisations, products, upgrades })),
catchAppError(error => of(AdminOrganisationListActions.loadOrganisationsFailure({ error })))
))
));
Update: The following code works to call the sub service:
loadOrganisations$ = createEffect(() => this.actions$.pipe(
ofType(AdminOrganisationListActions.loadOrganisations),
switchMap(() => combineLatest([
this.adminOrganisationService.list().pipe(map( orgs => {
orgs.map(o =>
this.adminService.getOrganisationAppFeatures(o.id).pipe(map(feature => {
return o;
})).subscribe());
return orgs;
})),
this.adminProductService.listProducts(),
this.adminProductUpgradeService.listProductUpgrades()]).pipe(
map(([organisations, products, upgrades]) => {
return AdminOrganisationListActions.loadOrganisationsSuccess({organisations, products, upgrades});
}),
catchAppError(error => of(AdminOrganisationListActions.loadOrganisationsFailure({ error })))
))
));
However, the line return AdminOrganisationListActions.loadOrganisationsSuccess({organisations, products, upgrades}); is executed before the features for each organization has finished loading.
combineLatest()works not as you expect. It will emit every time one of the input observables emits, and only after all of them produce a value at least once. If one of them errors, the result ofcombineLatest()also errors.You are not supposed to use
combineLatest()in an effect because it takes away the control of when the effect should happen.You should never subscribe inside an observable.
If you need the results of
adminOrganisationService.list()before you call another service, then you should do exactly this - call it first.Add
catchError()operators where needed.Check type of
featuresin your IDE.