MrWolfZ / ngrx-forms

Enhance your forms in Angular applications with the power of ngrx
MIT License
374 stars 111 forks source link

Having trouble integrating ngrx-forms in angular 8 #171

Closed dkowis closed 4 years ago

dkowis commented 4 years ago

my sample project: https://github.com/dkowis/ngrx-test/tree/master

We're following this pattern: https://ngrx.io/guide/store/recipes/injecting#injecting-reducers

Following the example here: https://github.com/ngrx/platform/blob/master/projects/example-app/src/app/app.module.ts#L40 and https://github.com/ngrx/platform/blob/master/projects/example-app/src/app/reducers/index.ts#L36

Symptoms are that the form actions are fired, but never reduced. I'm not sure how to hook up the form reducer.

EDIT: significantly fixed up the sample project, demonstrates the problem perfectly.

MrWolfZ commented 4 years ago

I believe the issue lies here:

export const formReducer = createReducer<FormGroupState<InfoForm>>(
  initialFormState,
  onNgrxForms()
);

onNgrxForms only works if the form state is a property of the state you create a reducer for (like the infoForm in your appReducer), not if it is the state itself. The reason for this is that there already is a reducer for the form state that is exported by ngrx-forms: formStateReducer. If you want a form state to be on the root state, you can just use that. However, in larger applications (which is what Angular is best suited for) you will most likely have your forms located with other state instead of directly on the root state.

Therefore, you have two options.

1) use formStateReducer directly:

import { formStateReducer } from 'ngrx-forms';

export const ROOT_REDUCER = new InjectionToken<ActionReducerMap<AppState, Action>>(
  'Root reducers token', {
    factory: () => ({
      thing: someReducer,
      infoForm: (s, a) => formStateReducer(s || initialFormState, a),
      moreThings: someReducer,
    }),
  });

See this sandbox for a running example of this.

2) Nest your form state in a larger state:

export interface AppState {
  info: InfoState;
  // ...
}

export interface InfoState {
  infoForm: FormGroupState<InfoForm>;
  otherInfoStuff: string;
}

export const infoReducer = createReducer<InfoState>(
  initialInfoState,
  onNgrxForms()
);

export const ROOT_REDUCER = new InjectionToken<
  ActionReducerMap<AppState, Action>
>("Root reducers token", {
  factory: () => ({
    thing: someReducer,
    info: infoReducer,
    moreThings: someReducer
  })
});

See this sandbox for a running example.

dkowis commented 4 years ago

Aha, this makes a lot of sense. I don't know how this would be added to the documentation, but could it be somehow? Thanks!