Recently NGRX/Entities have been introduced:
https://medium.com/ngrx/introducing-ngrx-entity-598176456e15 https://github.com/ngrx/platform/tree/master/example-app
And since they are made in a way that an adapter handles a (read: one, single) map-datastructure and on initialization then gets the rest of the reducer state, I was wondering...
Is it possible to hold multiple Entities in one reducer/adapter? The interface says no but maybe there is a hack or it is planned for the future? What if I already have multiple maps in one reducer? Am I forced to split it up or avoid the Entities feature?
The answer below is valid. An alternative approach for (in this example lazily loaded, but not necessarily) modules is to combine reducers with ActionReducerMap:
in your lazy.module.ts:
export interface LazyState {
lazyAState: LazyAState;
lazyBState: LazyBState;
}
export const lazyReducers: ActionReducerMap<LazyState> = {
lazyA: lazyAReducer,
lazyB: lazyBReducer
};
export interface AppState extends forRoot.AppState {
lazy: LazyState;
}
@NgModule({
imports: [
LazyRoutingModule,
StoreModule.forFeature('lazy', lazyReducers),
EffectsModule.forFeature([LazyEffects])
]
})
export class LazyModule {
static forRoot() {
return {
ngModule: LazyModule,
providers: [],
};
}
}
And in lazy.selectors.ts (you import the adapters from the reducer-file):
export const getLazyState = createFeatureSelector<LazyState>('lazy');
const getLazyAState = createSelector(getLazyState, (state: LazyState) => state.lazyAState);
const getLazyBState = createSelector(getLazyState, (state: LazyState) => state.lazyBState);
const {selectEntities: lazyASelectEntities, selectAll: lazyASelectAll} = LAZY_A_ADAPTER.getSelectors();
export const lazyADictionary = createSelector(getLazyAState, lazyASelectEntities);
export const lazyAArray = createSelector(getLazyAState, lazyASelectAll);
export const lazyASomeOtherAttributeNotFromAdapter = createSelector(getLazyAState, (state: LazyAState) => state.ids as string[]);
const {selectEntities: lazyBSelectEntities, selectAll: lazyBSelectAll} = LAZY_B_ADAPTER.getSelectors();
// same as for lazy-A, you can also combine selectors if you want
NgRx Entity is a small library that helps us to keep our entities in this ideal Entity state format (array of ids plus map of entities). This library is designed to be used in conjunction with NgRx Store and is really a key part of the NgRx ecosystem.
An entity adapter is a plain JS object (not a class) containing the generated reducer functions, the original provided selectId and sortComparer callbacks, a method to generate an initial "entity state" value, and functions to generate a set of globalized and non-globalized memoized selector functions for this entity ...
Entity Adapter NgRx has EntityAdapterinterface that provides many collection methods for managing the entity state. It is instantiated using NgRx createEntityAdaptermethod as following. article.adapter.ts
When you're using NgRx Entity, most of the reducer's cases will be as lean as in the snippet above. Within each case, you will use one of the adapter's functions to mutate the current state in a pure way. While the primary focus of NgRx Entity is to use the package in a state reducer, its generic API allows it to be used in more scenarios.
This selectors are then ready to be used directly in a component or as a starting point for building other selectors. Though the ngrx Entity allows the writing of state, reducer and selector logic to be easier, the reducer function itself still has to be written. Using the Ngrx Entity does not avoid having to write reducer logic for each entity.
The entity adapter also provides methods for operations against an entity. These methods can change one to many records at a time. Each method returns the newly modified state if changes were made and the same state if no changes were made.
NgRx entity is a simple and small library to handle large arrays even thought the documentation does not explain how to use more than one entity in one state, it should be easy since the library what it does behind the scenes is just normalize the array and create a dictionary with the data.
In order to make work the state with one or more entities follow the next steps:
Start by defining a state of each entity.
interface CarState extends EntityState<Car> {
total: number;
}
interface PlaceState extends EntityState<Place> {
total: number;
}
Then create a state that holds the entities
export interface State {
msg: string;
cars: CarState;
places: PlaceState;
}
Create the adapters for each entity state to manipulate the data and create the initial states.
const adapterCar = createEntityAdapter<Car>();
const adapterPlace = createEntityAdapter<Place>();
const carInitialState: CarState = adapterCar.getInitialState({ total: 0 });
const placeInitialState: PlaceState = adapterPlace.getInitialState({ total: 0 });
Define the initial global state
const initialState = {
msg: 'Multiple entities in the same state',
cars: carInitialState,
places: placeInitialState
}
Create the reducer:
export function reducer(state: State = initialState, action: ExampleActions): State {
switch (action.type) {
case ExampleActionTypes.GetCarList:
return { ...state, cars: adapterCar.addMany(action.payload, state.cars) };
case ExampleActionTypes.GetPlaceList:
return { ...state, places: adapterPlace.addMany(action.payload, state.places) };
default:
return state;
}
}
Expose the selectors
export const selectCarState = (state: State) => state.cars;
export const selectPlaceState = (state: State) => state.places;
export const { selectAll: selectAllCars } = adapterCar.getSelectors();
export const { selectAll: selectAllPlaces } = adapterPlace.getSelectors();
That's it :)
Live example: https://stackblitz.com/edit/angular-multiple-entities-in-same-state
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