diff --git a/projects/ngrx.io/content/guide/data/entity-metadata.md b/projects/ngrx.io/content/guide/data/entity-metadata.md index d57843a88a..f382eef166 100644 --- a/projects/ngrx.io/content/guide/data/entity-metadata.md +++ b/projects/ngrx.io/content/guide/data/entity-metadata.md @@ -149,7 +149,7 @@ Not every entity will have a primary key property named `id`. For some entities, In these cases, you specify a `selectId` function that, given an entity instance, returns an integer or string primary key value. -In the _EntityCollectionReducer_ [tests](https://github.com/ngrx/platform/blob/master/modules/data/spec/reducers/entity-collection-reducer.spec.ts), +In the _EntityCollectionReducer_ [tests](https://github.com/ngrx/platform/blob/master/modules/data/spec/reducers/entity-collection-reducer.spec.ts), the `Villain` type has a string primary key property named `key`. The `selectorId` function is this: @@ -206,7 +206,7 @@ Each NgRx Data entity collection in the the store has You can add your own collection properties by setting the `additionalCollectionState` property to an object with those custom collection properties. -The _EntitySelectors_ [tests](https://github.com/ngrx/platform/blob/master/modules/data/spec/selectors/entity-selectors.spec.ts) +The _EntitySelectors_ [tests](https://github.com/ngrx/platform/blob/master/modules/data/spec/selectors/entity-selectors.spec.ts) illustrate by adding `foo` and `bar` collection properties to test hero metadata. ```typescript @@ -220,6 +220,92 @@ The property values become the initial collection values for those properties wh The NgRx Data library generates selectors for these properties but has no way to update them. You'll have to create or extend the existing reducers to do that yourself. +If the property you want to add comes from `backend`, you will need some additional work to make sure the property can be saved into store from `Effects` correctly. + + 1. You need to create a `AdditioanlPersistenceResultHandler extends DefaultPersistenceResultHandler` and overwrite [handleSuccess](../../../../../../modules/data/src/dataservices/persistence-result-handler.service.ts) method, the purpose is to parse the data got from `DataService` and retrieve the additional property then save it to `action.payload`. Here is a sample. + + + + ```typescript +export class AdditionalPropertyPersistenceResultHandler { + handleSuccess(originalAction: EntityAction): (data: any) => Action { + const actionHandler = super.handleSuccess(originalAction); + // here will return a factory to get a data handler to + // parse data from DataService and save to action.payload + return function(data: any) { + const action = actionHandler.call(this, data); + if (action && data && data.foo) { + // save the data.foo to action.payload.foo + (action as any).payload.foo = data.foo; + } + return action; + }; + } +} +``` + + 2. Now the additional property is set to `action.payload`, but we still need to set it to the instance of EntityCollection in `reducer`, to do that, we need to create a `AdditionalEntityCollectionReducerMethods extends EntityCollectionReducerMethods`, and overwrite the method match your `action`, for example, if the additional property `foo` only available in `getWithQuery action`, we can do like this. + + ```typescript +export class AdditionalEntityCollectionReducerMethods extends EntityCollectionReducerMethods { + constructor(public entityName: string, public definition: EntityDefinition) { + super(entityName, definition); + } + protected queryManySuccess( + collection: EntityCollection, + action: EntityAction + ): EntityCollection { + const ec = super.queryManySuccess(collection, action); + if ((action.payload as any).foo) { + // save the foo property from action.payload to entityCollection instance + (ec as any).foo = (action.payload as any).foo; + } + return ec; + } +} +``` + + 3. Finally we need to register the `AdditionalPersistenceResultHandler` and `AdditionalEntityCollectionReducerMethods` to replace the default implementation. + + * Register `AdditionalPersistenceResultHandler` in `NgModule`, + + ```typescript +@NgModule({ + ... + { provide: PersistenceResultHandler, useClass: PagePersistenceResultHandler }, +}) +``` + + * Register `AdditionalEntityCollectionReducerMethods`, to do that, we need to create a `AdditionalEntityCollectionReducerMethodFactory`, for detail, please see this [doc](./entity-reducer.md) + + ```typescript +@Injectable() +export class AdditionalEntityCollectionReducerMethodsFactory { + constructor(private entityDefinitionService: EntityDefinitionService) {} + /** Create the {EntityCollectionReducerMethods} for the named entity type */ + create(entityName: string): EntityCollectionReducerMethodMap { + const definition = this.entityDefinitionService.getDefinition(entityName); + const methodsClass = new AdditionalEntityCollectionReducerMethods(entityName, definition); + return methodsClass.methods; + } +} +``` + + then register this `AdditionalEntityCollectionReducerMethodsFactory` to `NgModule`, + + ```typescript +@NgModule({ + ... + { + provide: EntityCollectionReducerMethodsFactory, + useClass: AdditionalEntityCollectionReducerMethodsFactory + }, +}) +``` + + Now you can get `foo` from `backend` just like other `EntityCollection` level property. + + ## Pluralizing the entity name