Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ngrx/data- How can I write custom reducers to handle endpoint returning multiple entities?

I am currently trying to convert my ngrx store to use ngrx/data to handle my entities. One of the trickier obstacles I have hit is handling API endpoints that return data for multiple entities. A simple example- lets say I have the following models that can be retrieved from my API:

export interface Parent {
  id: string;
  name: string;
}

export interface Child {
  id: string;
  name: string;
  parent: string
}

And I have the following endpoints:

/api/parents              #GET (list of parents), POST (create new parent)
/api/parents/<PARENT_ID>  #GET, PATCH, PUSH, DELETE (a single parent)
/api/children             #GET (list of children), POST (create new child)
/api/children/<CHILD_ID>  #GET, PATCH, PUSH, DELETE (a single child)
/api/families             #GET (all parents and children)

/api/families is a convenience function, that returns all parents and children in the format:

{
  parents: Parent[];
  children: Child[];
}

In the past, I have made a separate families entry for my ngrx store with just loading/loaded parameters, then a set of LoadFamilies actions to grab the data from the server. I then include those actions in my Parents and Children store reducers, and act on them appropriately. What I want to do now is add this additional reducer to my existing ngrx/data controlled entities. Any thoughts/example code on how to go about this?

like image 835
MarkD Avatar asked Aug 16 '19 20:08

MarkD


People also ask

Can we have multiple stores in NgRx?

The objective of this article is to provide a technical implementation of the NGRX for an application with a complexity that could benefit from adding feature store(s) in addition to the root store.

What is the use of reducer in NgRx?

Reducers in NgRx are responsible for handling transitions from one state to the next state in your application. Reducer functions handle these transitions by determining which actions to handle based on the type.

What are angular reducers?

Reducers are pure functions accepting two arguments, the previous state and an Action. When an Action is dispatched ngrx goes through all the reducers passing as arguments the previous state and the Action, in the order that the reducers where created, until it finds a case for that action.

What is NgRx data?

NgRx Data is an extension of NgRx state management system for angular framework. The advantage of using NgRx Data is simplicity and a drastic code reduction in compare to traditional approach. Of course the world isn't perfect and you can't use this extension everywhere.


1 Answers

Extend DefaultDataService see https://ngrx.io/guide/data/entity-dataservice#custom-entitydataservice

Within a pipe map nested data to entities (I use normalizr)

Then dispatch entity cache action(s) using EntityActionFactory as eaFactory to update store as you see fit

example with Parent <--> Child of ConcreteRecordHeader <--> ConcreteRecordLoad

  getById(key: string | number): Observable<ConcreteRecordHeader> {
    return super.getById(key).pipe(
      map((denorm: any) =>
        this.categoryCacheService.normalizeCategory([denorm])
      ),
      tap((norm: NormalizedCategories) => this.mergeQuerySet(norm)),
      map((norm: NormalizedCategories) => this.pickEntities(norm)[0])
    ) as Observable<ConcreteRecordHeader>;
  }

  protected mergeQuerySet(norm: NormalizedCategories) {
    console.log("⚠️ Merging server data ⚠️");
    const options: EntityActionOptions = {
      mergeStrategy: MergeStrategy.PreserveChanges
    };
    const data = norm.entities.concreteRecordLoads
      ? Object.values(norm.entities.concreteRecordLoads)
      : [];
    const action = this.eaFactory.create(
      "ConcreteRecordLoad",
      EntityOp.SAVE_UPSERT_MANY_SUCCESS,
      data,
      options
    );
    this.entityCacheDispatcher.dispatch(action);
  }

  protected pickEntities(norm: NormalizedCategories) {
    return (norm.entities.concreteRecordHeaders
      ? Object.values(norm.entities.concreteRecordHeaders)
      : []) as ConcreteRecordHeader[];
  }

Sadly this.entityCacheDispatcher.mergeQuerySet() isn't working with mergeStrategy which is the natural API to use. I plan on submitting a PR for this.

like image 167
Andrew Allen Avatar answered Sep 22 '22 13:09

Andrew Allen