8

I'd like to write just one action to perform the same CRUD operations on state, just on different slices of it, while preserving type safety.

For example, I'd like to use the following action to apply a set operation to any slice with a generic type T:

export class Set_Entity<T> {
  static readonly type = '[Entity] Set';
  constructor(public payload: <T>) {}
}

This is problematic because the type will always be the same. Is it possible to somehow decorate this class so a unique type property can be passed in whenever it is used as the @Action?

Something like:

/* action* /
class Set_Entity<T> {
  constructor(public entity: string, public payload: <T>) {}
}


/* state */
@Action(Set_Entity('[Groups] Set Group'/* <-- Changes the `type` property */))
set_group(
  context: StateContext<Model>, 
  action: SetEntity<{entity: string, payload: Group}>,
) {
  const entity = action.entity;
  const data = action.payload;
  context.patchState({ [entity]: data });
}


/* facade or something */
this.store.dispatch([
  new Set_Entity<GroupEntityType>(
    'user', // <-- the state slice
    aRecord,
  ),
]);


Even this solution leaves more to be desired. Generic Actions still must be written for each state slice, for each CRUD operation. It would be nice to be able to use the same generic action for each CRUD op on each state slice.

I managed to do it beautifully with NGRX via typescript-fsa and typescript-fsa-reducers. Only needed one single generic action plus one single generic reducer for the entire state, all typesafe.

The action looked like this:

function generic_set_action<T>(sliceName: string): ActionCreator<T> {
  const creator = actionCreatorFactory(sliceName);
  const action = creator<T>('set')
  return action; // Produces type of  `sliceName/set`
}

// Create the action
generic_set_action<User>('sliceName')(payload)

The reducer:

export function create_generic_reducer<T>(sliceName: string) {
  const action_set = generic_set_action<T>(sliceName);

  return reducerWithInitialState({} as T)
    .case(action_set, (state, data) => (data))
    .build();
}

And finally when creating the reducers:

export const Reducers: ActionReducerMap<State> = {
  coolSlice: create_generic_reducer<MySliceModel>('coolSlice'),
  // repeat for each slice..
};

It would be great to be able to reproduce this with NGXS.

skwny
  • 2,930
  • 4
  • 25
  • 45
  • no, this is not possible, as NGXS looks for action handlers by `type` property, so the `type` property cannot be dynamic, I guess you could try that approach in your example with the `SetEntity` action, but it cannot be used in different states, as multiple handlers will be run – overthesanity Jul 22 '19 at 08:39

0 Answers0