I have an Angular 9 application that for the most part uses observables. In one component I need to:
To do this I originally had the following code (which appears to work):
getPopulatedResponses(): Observable<ResponseModel[]> {
return this.companyFilesStore
.select(companyFilesSelector)
.pipe(
switchMap(companies => {
return this.getAccessibleResponses(companies);
})
)
}
getAccessibleResponses(accessibleCompanies: CompanyFilesModel[]): Observable<ResponseModel[]> {
return this.responsesStore
.select(responsesSelector)
.pipe(
map((responses) => {
return responses?.map((response) => {
const company = accessibleCompanies?.find(c => c.companyGuid === response.companyId);
response.companyName = company?.companyName;
return response;
}).sort((a, b) => {
return a.completedDateTime < b.completedDateTime ? 1 : -1;
})
})
)
Firstly I'm not sure if switchMap was the right operator because if the companyFilesSelector was updating and then the responsesSelector kicked in part way through would that not terminate the previous subscription?
And now I need to also add a subscription to a filter service that I have so I've made the following changes to the getAccessibleResponses method (which again appear to be working but I feel like it may be more through luck/good connection than anything else):
getAccessibleResponses(
accessibleCompanies: CompanyFilesModel[],
): Observable<ResponseModel[]> {
return this.searchAndFilterService.getFilter()
.pipe(
switchMap(filter => {
return this.responsesStore
.select(responsesSelector)
.pipe(
map((responses) => {
return responses?.map((response) => {
const company = accessibleCompanies?.find(c => c.companyGuid === response.companyId);
response.companyName = company?.companyName;
return response;
})
?.filter(r => !r.isArchived)
?.filter(r => !filter?.selectedCompany || r.companyId === filter.selectedCompany.companyId)
?.filter(r => !filter.selectedAssessmentTemplate ||
r.assessmentTemplateId === filter.selectedAssessmentTemplate.assessmentTemplateId)
.sort((a, b) => {
return a.completedDateTime < b.completedDateTime ? 1 : -1;
})
})
)
})
)
}
I'm fairly confident this is not the best or even right approach but I'm getting a bit lost and struggling to understand how to achieve the following:
Any help or guidance would be greatly appreciated.
As far as I can tell, you have 3 main parts to work with(companies
, responses
and filter
) and whenever one of these parts changes(e.g emits a new value), you'd like to recompute the data based on what's changed.
If that's the case and assuming that your app state is held in a single source of truth, here is how I'd do it:
const companies$ = this.store.select(companyFilesSelector);
const accessibleResponses$ = this.store.select(responsesSelector);
const activeFilter$ = this.store.select(activeFilterSelector);
const data$ = combineLatest(
companies$,
accessibleResponses$,
activeFilter$,
).pipe(
map(([companies, responses, filter]) => {
// Logic here...
})
)
combineLatest
is used here because you want to compute the shown data whenever one of the 3 parts changes.
So, if your initial state might be:
{
companies: [],
responses: [],
activeFitler: null,
}
Whenever you add, for example, some new companies, the function provided to map
will be invoked in order to update what will be shown to the user depending on the new companies. The same happens when you add new responses
or a new filter
.
could I use a switchMap from that so no subscription looks at that store to potentially save memory?
I'm don't think it saves much memory, as all what switchMap
does it to unsubscribe from the active observable and create a new one, based on the newly arrived value and the provided function.
Also, since you're subscribing to the store, the first data retrieval should happen synchronously since I suppose the store is a sort of BehaviorSubject
(e.g NgRx)