I have this situation where after calling MainDataSourceGet() in the code below, I need to call a second service - stateGet$() - to get additional data for each item of type "type1".
My question is: is subscribing to forkJoin a bad practice? If I dont do it, the the second api will not be consumed and the dictionary with the statusResults will be empty.
I added some comments to the code, hope it makes sense.
const stateGet$ = (x: model) => {
//need to do this split as part of the logic
let compositeVals = x.ref.split(':');
let id = compositeVals[0];
let propertySet = compositeVals[1];
return this.myService.apiStatesGet$({id, propertySet});
}
return this.myService
//call first (main) api
.MainDataSourceGet()
.pipe(
take(1),
//ignore all if there is no result
filter(ds => ds.Data && ds.Data.length > 0),
//from this point I want to consume "stateGet$" on a item by item basis
//with the sole purpose of calling this.addToStore() passing statuses as well
switchMap((data) => {
//filter 'type1 items'
let type1Data = data.filter(k => k.ref.includes('type1'));
//this dic will hold statuses
var dicIdStatus: { [id: string] : boolean; } = {};
//second api call
forkJoin(type1Data.map(v => stateGet$(v)
.pipe(
map(statusResult => {
dicIdStatus[statusResult.id] = statusResult.isActive;
}),
))
)
//without this subscribe() the pipe > map action will not run, and the dictionary will be empty
.subscribe();
return of({data, dicIdStatus});
}),
tap(data => {
let dataSources = data.data;
let type1Data = dataSources.filter(k => k.ref.includes('type1'));
//finally do the thing with both items and statuses dictionary
this.addToStore(type1Data, data.dicIdStatus);
}),
map(() => { return someValue; })
);
CodePudding user response:
Generally, the way to do this is to let switchMap
subscribe for you. It will subscribe to the observable you return.
Using a switchMap to return return of({data, dicIdStatus});
pretty much never makes sense. If you're just immediately emitting a single value, you may as well just use map.
These two are semantically pretty much the same:
switchMap(v => of({named: v}))
map(v => ({named: v}))
Instead if you return the observable that you'd like to subscribe to, you solve this problem. I can't test this for you, but that may look something like this:
const stateGet$ = (x: model) => {
//need to do this split as part of the logic
let compositeVals = x.ref.split(':');
let id = compositeVals[0];
let propertySet = compositeVals[1];
return this.myService.apiStatesGet$({id, propertySet});
}
//call first (main) api
return this.myService.MainDataSourceGet().pipe(
take(1),
//ignore all if there is no result
filter(ds => ds.Data && ds.Data.length > 0),
//filter 'type1 items'
map(data => data.filter(k => k.ref.includes('type1'))),
//from this point I want to consume "stateGet$" on a item by item basis
//with the sole purpose of calling this.addToStore() passing statuses as well
switchMap(type1Data =>
//second api call
forkJoin(
type1Data.map(stateGet$)
).pipe(
map(statusResults => {
//this dic will hold statuses
let dicIdStatus: { [id: string] : boolean; } = {};
statusResults.forEach(statusResult =>
dicIdStatus[statusResult.id] = statusResult.isActiv
)
return dicIdStatus;
}),
map(dicIdStatus => ({type1Data, dicIdStatus}))
)
),
tap(({type1Data, dicIdStatus}) =>
//finally do the thing with both items and statuses dictionary
this.addToStore(type1Data, dicIdStatus)
),
map(() => someValue)
);