Build Angular 2+ forms with Redux
npm install @phrixothrixa/angular-redux-form

This library is a thin layer of connective tissue between Angular 2+ forms and
Redux. It provides unidirectional data binding between your Redux state and
your forms elements. It builds on existing Angular functionality like
NgModel
and
NgControl
This supports both Template driven forms and Reactive driven forms.
For the simplest use-cases, the API is very straightforward. Your template
would look something like this:
``html`
The important bit to note here is the [connect] directive. This is the only thingconnect
you should have to add to your form template in order to bind it to your Redux state.
The argument provided to is basically a path to form state inside of your
overall app state. So for example if my Redux app state looks like this:
`json`
{
"foo": "bar",
"myForm": {
"address": "1 Foo St."
}
}
Then I would supply myForm as the argument to [connect]. If myForm were nested
deeper inside of the app state, you could do something like this:
`html`
Note that ImmutableJS integration is provided seamlessly. If personalInfo is anget()
immutable Map structure, the library will automatically use or getIn() to
find the appropriate bits of state.
Then, in your application bootstrap code, you need to add a provider for
the class that is responsible for connecting your forms to your Redux state.
There are two ways of doing this: either using an Redux.Store object orNgRedux
an object. There are no substantial differences between these
approaches, but if you are already using
@angular-redux/store or you wish to integrate
it into your project, then you would do something like this:
`typescript
import { NgReduxModule } from '@angular-redux/store';
import { NgReduxFormModule } from '@angular-redux/form';
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
FormsModule,
NgReduxFormModule,
NgReduxModule,
],
bootstrap: [MyApplicationComponent],
})
export class ExampleModule {}
`
Or if you are using Redux without @angular-redux/store, then your bootstrap call would look
more like this (substitute your own store creation code):
`typescript
import { provideReduxForms } from '@angular-redux/form';
const storeCreator = compose(applyMiddleware(logger))(createStore);
const store = create(reducers,
@NgModule({
imports: [BrowserModule, ReactiveFormsModule, FormsModule, NgReduxFormModule],
providers: [provideReduxForms(store)],
bootstrap: [MyApplicationComponent],
})
export class ExampleModule {}
`
The essential bit of code in the above samples is the call to provideReduxForms(...).@angular-redux/form
This configures and provides access to your Redux store or NgReduxprovideReduxForms
instance. The shape of the object that expects is very
basic:
`typescript
export interface AbstractStore
/// Dispatch an action
dispatch(action: Action & { payload? }): void;
/// Retrieve the current application state
getState(): RootState;
/// Subscribe to changes in the store
subscribe(fn: () => void): Redux.Unsubscribe;
}
`
Both NgRedux and Redux.Store conform to this shape. If you have a moreAbstractStore
complicated use-case that is not covered here, you could even create your own store
shim as long as it conforms to the shape of .
The bindings work by inspecting the shape of your form and then binding to a Redux
state object that has the same shape. The important element is NgControl::path.path
Each control in an Angular 2 form has a computed property called which uses
a very basic algorithm, ascending the tree from the leaf (control) to the root
(the
element has a connect directive that points to the state element
form1. This means that the children within your form will all be bound to some
bit of state inside of the form1 object in your Redux state. Then we have a child
input which is bound to a property called fullname. This is a basic text box. If
you were to inspect it in the debugger, it would have a path value like this:`
['form1', 'fullname']
`And therefore it would bind to this piece of Redux state:
`json
{
"form1": {
"fullname": "Chris Bond"
}
}
`So far so good. But look at the array element inside our form, in the
element. It is bound to an array property called dependents. The elements inside
of the tag contain the template that will be instantiated for each
element inside of the dependents array. The ngModelGroup specifies that we should
create a FormGroup element for each item in the array and the name of that group
should be the value of index (the zero-based index of the element that is being
rendered). This is important because it allows us to create a form structure that
matches our Redux state. Let's say our state looks like this:`json
{
"form1": {
"fullname": "Chris Bond",
"dependents": [
{
"fullname": "Christopher Bond Jr.",
"type": "biological"
}
]
}
}
`If you think about the 'path' to the first element of the dependents array, it would
be this:
`
['form1', 'dependents', 0]
`The last element,
0, is the index into the dependents array. This is our
ngModelGroup element. This allows us to create a form structure that has the
same structure as our Redux state. Therefore if we pause the debugger and look at
the path property on our first Note: If you implement your own reducer instead of using the default one provided by
ng2-form-redux, the state you return still needs to match the shape of your form,
otherwise data-binding is not going to work. This is why it probably makes sense to
just use the default reducer in almost every case - because your custom reducer would
have to implement the same logic and produce a state object that is the same shape.
But if you are having trouble with the default reducer, or if you find the fact that
you have to use
composeReducers distasteful, then this is another route available
to you.The unit tests in
*.test.ts files also contain useful examples of how to build
forms using @angular-redux/form`.