Angular NGRX: multiple Entities in one EntityAdapt

2020-07-03 07:43发布

问题:

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

回答1:

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



标签: angular ngrx