Reactive Angular Forms with NGRX

Austin
2 min readDec 29, 2017

--

Reactive forms are great, NGRX is great but how do you use them together? I’m going to show you the different ways to put these two pieces of technology together.

The problem

When we subscribe to our store in our components, it returns a observable. On the other hand, our reactive forms expect a raw object to bind the values to it. You probably have something like this somewhere in your application.

@Component({ ... })
export class MyComponent {
constructor(private store: Store<AppState>, private formBuilder: FormBuilder) {} ngOnInit() {
const form$ = this.store.select(state => state.form);
const formBuilder = this.formBuilder.group({
name: ['']
});
}
}

As you can see here, I have my form state but I can’t bind it to my form builder because its a observable.

How do we solve this?

There are 3 different ways I’ve approached this problem before:

  1. Create a child component that contains the form that the host component binds it with a input and async pipe.
@Component({
template: `
<my-form [data]="form$ | async"></my-form>
`
})
export class MyComponent {
form$ = this.store.select(state => state.form);
constructor(private store: Store<AppState>) {}
}

This is a nice approach but it creates extra boiler plate.

2. Subscribe to observable and patch the value.

@Component({ ... })
export class MyComponent {
constructor(private store: Store<AppState>, private formBuilder: FormBuilder) {}
ngOnInit() {
const form$ = this.store.select(state => state.form);
const formBuilder = this.formBuilder.group({
name: ['']
});
this.form$.subscribe(data => formBuilder.patchValue(data));
}
}

This doesn’t introduce any extra boilerplate but now we have to deal with unsubscribing from the observable.

3. Create a custom directive to bind the form automatically.

@Directive({ selector: '[connectForm]' })
export class ConnectFormDirective {
@Input('connectForm')
set data(val: any) {
if (val) {
this.formGroupDirective.form.patchValue(val);
this.formGroupDirective.form.markAsPristine();
}
}
constructor(private formGroupDirective: FormGroupDirective) {}
}

then in our form we can just hook it up really cleanly like:

@Component({
template: `
<form [formGroup]="form" [connectForm]="form$ | async">...</form>
`
})
export class MyComponent {
form$ = this.store.select(state => state.form);
form = this.formBuilder.group({
name: ['']
});
constructor(private store: Store<AppState>) {}
}

Pretty sweet huh?

I’m #ngPanda

I hope you enjoyed the post, if you liked it follow me on Twitter and Githubfor more JavaScript tips/opinions/projects/articles/etc!

--

--

Austin

Crafter of Software • Lover of #JavaScript #goldendoodles & #opensource