3
votes

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:

  1. How can I at the same time use ngrx/data and load (related) model data with ngrx/effects or EntityEffects?
  2. Where do I extend the appropriate services/classes without having to implement every reducer/action manually and instead letting ngrx/data take care of that?

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/

1

1 Answers

1
votes

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[]
                  })
                )
              );
          })
        );
      })
    )
  );

Potential roadmap

  1. Create your custom action e.g. type "[OrderProductsPage ONINIT] ONINIT"
  2. Create effect that calls saveEntities
// 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`)
  1. Extend 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.Add

Reference to super.saveEntities()

Update

This works but its a bit more hacky than I'd like

Stackblitz