When converting an application to make use of ngrx state management, I decided to manage the state of some models with ngrx/data via EntityMaps. This works fine so far, however when things get a little more complex, e.g. based on fetched model data from a remote server I need related models (orders->products or in REST endpoint format GET orders/:id/products)
I feel stuck - I know there is ngrx/effects to trigger actions that load other data when using ngrx, and in this ngrx/data guide, `EntityEffects are mentioned to trigger side effects with actions, but other than that the docs seem to be in progress and are not of much help to me now.
Unfortunately, there is not much about extending the EntityEffects to customize the fetching of remote data. Of course, I could go all the way down implementing selectors, actions, reducers, effects the standard ngrx way, but isn't the whole point of ngrx/data to make state handling easier with less boilerplate code and act kind of as a wrapper around ngrx, hiding the default/boilerplate code but still providing a way to use the underlying parts?
Since I am relatively new to ngrx and state management in general, I don't know where to exactly look for best practices or a recommended solution, how this problem is handled.
So my questions basically are:
I already searched Stackoverflow, Gitter and asked colleagues, but with no success - or do I make it way more complicated than it has to be? If so, I am thankful for any help.
Note: This question is especially about ngrx/
This is an open github issue https://github.com/ngrx/platform/issues/1934
However I believe there is a route to doing this without starting completely from scratch using the EntityCacheDispatcher's saveEntities method Save with EntityCacheDispatcher.saveEntities().
To give you an idea how this works when used as intended, here is my save order effect on a recent project
saveOrder$ = createEffect(() =>
this.actions$.pipe(
ofType(PurchaseOrderActions.saveOrder),
switchMap(() => {
const order$ = this.store.pipe(select(selectHeader));
const orderLines$ = this.store.pipe(select(allLines));
const deletedOrderLines$ = this.store.pipe(select(selectDeletedLines));
return combineLatest([order$, orderLines$, deletedOrderLines$]).pipe(
first(),
switchMap(([order, lines, deletedLines]) => {
const changes: ChangeSetItem[] = [
cif.upsert("PurchaseOrder", order),
cif.upsert("PurchaseOrderLine", lines),
cif.delete("PurchaseOrderLine", deletedLines)
];
return this.entityCacheDispatcher
.saveEntities(changes, `${this.baseURL}order`)
.pipe(
map(changesResponse =>
PurchaseOrderActions.saveOrderSuccess({
header: changesResponse.changes[0]
.entities[0] as PurchaseOrder,
lines: changesResponse.changes[1]
.entities as PurchaseOrderLine[]
})
)
);
})
);
})
)
);
"[OrderProductsPage ONINIT] ONINIT"// include in custom action payload and get this id here
const id;
// changeSet with empty changes
const changeSet = {
changes: [],
tag: "CUSTOM_GET_PRODUCTS" // hook for defining custom behaviour
}
this.entityCacheDispatcher.saveEntities(changeSet, `orders/${id}/products`)
EntityCacheDataService and override the saveEntities method to first check if tag is CUSTOM_GET_PRODUCTS otherwise call super.saveEntities(changeSet, url). If tag is CUSTOM_GET_PRODUCTS than call get http and pipe map to changeSet with ChangeSetOperation.AddReference to super.saveEntities()
This works but its a bit more hacky than I'd like
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With