Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@ngrx/data - how can I extend a collection reducer / replace handling of the results of a data service call?

Tags:

Using @ngrx/data I want to handle the result of the getWithQuery API call differently than the default.

Currently if this returns an array of entities, this gets loaded in the entityCache directly.

So far I've used the standard pattern shown in the overview:

export const entityMetadata: EntityMetadataMap = {
  PurchaseOrder: {}
};
@Injectable({
  providedIn: "root"
})
export class PurchaseOrderService extends EntityCollectionServiceBase<
  PurchaseOrder
> {
  constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
    super("PurchaseOrder", serviceElementsFactory);
  }
}

Instead I want to handle the following API response and load the entities in the entityCache as the normal getWithQuery would but also stick the total elsewhere in my store.

{
    "entities": [{...}, {...}, ..., {...}],    // list of entities
    "total": 100
}

Naturally I get the following error if this API response is returned:

enter image description here

My understanding is that a default reducer gets created and registered for each entity providing the EntityCollectionDataService interface with the add / delete / getAll / getById / getWithQuery / update methods.

I want to keep these methods but override the getWithQuery reducer to achieve my aim.

This is mentioned in Customizing Entity Reducer Behavior

But quite often you'd like to extend a collection reducer with some additional reducer logic that runs before or after.

How can this be actually done?

I still get the above error if I try to override getWithQuery inside my PurchaseOrderService

  getWithQuery(params) {
    return super.getWithQuery(params).pipe(tap(result => console.log(result)));
  }
like image 835
Andrew Allen Avatar asked Nov 27 '19 15:11

Andrew Allen


People also ask

What does a reducer do 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.

Where does NgRx store its data?

Where Does NgRx Store Data? NgRx stores the application state in an RxJS observable inside an Angular service called Store. At the same time, this service implements the Observable interface.

What does reducer do in angular?

Reducers are responsible for handling transitions between states. Reducers react to the Actions dispatched and executes a pure function to update the Store. Pure functions are functions that are predictable and have no side effects.


1 Answers

Managed to get this working using a custom EntityDataService

@Injectable()
export class PurchaseOrderDataService extends DefaultDataService<
  PurchaseOrder
> {
  constructor(
    http: HttpClient,
    httpUrlGenerator: HttpUrlGenerator,
    logger: Logger,
    config: DefaultDataServiceConfig
  ) {
    super("PurchaseOrder", http, httpUrlGenerator, config);
  }

  getWithQuery(params: string | QueryParams): Observable<PurchaseOrder[]> {
    return super.getWithQuery(params).pipe(
      tap(res => console.log(res)),
      map((res: any) => res.entities)
    );
  }
}

Then this needs to be registered:

@NgModule({
  providers: [PurchaseOrderDataService] // <-- provide the data service
})
export class EntityStoreModule {
  constructor(
    entityDataService: EntityDataService,
    purchaseOrderDataService: PurchaseOrderDataService
  ) {
    entityDataService.registerService(
      "PurchaseOrder",
      purchaseOrderDataService
    ); // <-- register it
  }
}

And import this alongside EntityDataModule.forRoot({ entityMetadata }),

like image 132
Andrew Allen Avatar answered Oct 03 '22 09:10

Andrew Allen