Why and how to manage state for Angular Reactive Forms
Tags: angular, forms, ngrx, stateadapt
Published: 2021-04-12 08:34:37

---
title: Why and how to manage state for Angular Reactive Forms
published: true
description: Why and how to manage state for Angular Reactive Forms
tags: Angular, Forms, NgRx, StateAdapt
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbvz6coz9tmbj1zhuat2.png
series: Angular Reactive Forms
---
Should form state always be local? Some believe so, but methods like `setValue()` and `disable()` indicate that at least _sometimes_ you need to manage form state from the outside. But these methods are imperative and [reminiscent of jQuery](https://dev.to/this-is-angular/isn-t-angular-reactive-forms-basically-just-jquery-22ol), so usually it is better to have something more declarative and reactive like the rest of Angular. 

Before we look at how to accomplish this, let's review some scenarios where reactive state management is especially helpful:

### Saved Progress

This is any app where users spend a lot of time in a form and switch between multiple drafts, such as email clients with draft emails. This requires a lot of external state updates to the form.

### Undo/Redo

Undo/Redo across multiple form elements can be convenient for users, and it requires completely swapping out form state multiple times. 

### Time and Realtime

Sometimes realtime data needs to be reflected in a form on an ongoing basis, like when a user is entering a bid for an item with frequent price updates, or scheduling something in the near future. 

### Server-Side Events

Any time something happens to a data model that the user is editing, you may need to update the state of the form. These events could be another user editing the same data, an item selling out, some status change, or anything that would cause a problem if the user tried to submit outdated data. 

### Complexity

Complex forms are hard to understand, but Redux Devtools can help a lot. You have to personally decide when a form is complex enough to justify the time to set up external state management. 

# How?
## The Simple Way
Setting up external state management for forms might take a lot less time than you might think. I'm going to explain how to do it with both NgRx and StateAdapt because I just released StateAdapt and want you to know how cool it is :grin:

Skip to Steps 5-7 if you want to only see the stuff that is related to forms. 

### Step 1 (NgRx and StateAdapt)
Create the form state interface and initial state:
```typescript
// form-state.interface.ts
export interface FormState { // Whatever it is
  name: string;
  age: number;
}

export const initialState: FormState = { name: '', age: 0 };
```

### Step 2 (NgRx only)
Create this action:
```typescript
// form.actions.ts
import { createAction, props } from '@ngrx/store';
import { FormState } from './form-state.interface';

export const formValueChange = createAction(
  '[Form] Value Change',
  props<FormState>()
);
```

### Step 3 (NgRx only)
Create the reducer:
```typescript
// form.reducer.ts
import { Action, createReducer, on } from "@ngrx/store";
import { FormState, initialState } from "./form-state.interface";
import { formValueChange } from "./form.actions";

const formReducer = createReducer(
  initialState,
  on(formValueChange, (state, { type, ...update }) => ({ ...state, ...update }))
);

export function reducer(state: FormState | undefined, action: Action) {
  return formReducer(state, action);
}

```

### Step 4 (NgRx only)
Plug the reducer into the reducer/state tree, wherever you want it to show up (see [NgRx Docs](https://ngrx.io/guide/store/reducers#registering-root-state)). 

### Step 5

#### NgRx

Add these imports to the file of the component containing the form:
```typescript
import { using } from 'rxjs';
import { tap } from 'rxjs/operators';
import { formValueChange } from './form.actions';
```
Add this code inside the component class:
```typescript
  // this.form is the formGroup you created for the form
  formValues$ = using(
    () =>
      this.form.valueChanges
        .pipe(tap(values => this.store.dispatch(formValueChange(values))))
        .subscribe(),
    () => this.store.select(state => state.form) // Wherever you put it in your state tree
  );
```

#### StateAdapt

Add these imports to the file of the component containing the form:
```typescript
import { toSource } from '@state-adapt/rxjs';
import { adapt } from '@state-adapt/angular';
import { initialState } from './form-state.interface';
```
Add this code inside the component class:
```typescript
  // this.form is the formGroup you created for the form
  valueChanges$ = this.form.valueChanges.pipe(
    toSource('[Form] Value Change'),
  );
  formValues$ = adapt(initialState, {
    sources: { update: this.valueChanges$ },
  });
```

### Step 6 (NgRx and StateAdapt)
Drop this directive into your module:
```typescript
// patch-form-group-values.directive.ts
import { Directive, Input } from "@angular/core";

@Directive({
  selector: "[patchFormGroupValues]"
})
export class PatchFormGroupValuesDirective {
  @Input() formGroup: any;
  @Input()
  set patchFormGroupValues(val: any) {
    if (!val) return;
    this.formGroup.patchValue(val, { emitEvent: false });
  }
}
```

### Step 7 (NgRx and StateAdapt)
Use the new directive in your component template:
```html
<form [formGroup]="form" [patchFormGroupValues]="formValues$ | async">
  <input type="text" formControlName="name" />
  <input type="number" formControlName="age" />
</form>
```
## Simple Way Review
Here are working StackBlitz examples for [NgRx](https://stackblitz.com/edit/angular-reactive-forms-state-management?file=src%2Fapp%2Fform%2Fngrx-form.component.ts) and [StateAdapt](https://stackblitz.com/edit/angular-reactive-forms-state-management?file=src%2Fapp%2Fform%2Fstate-adapt-form.component.ts). Open up Redux Devtools and watch as you edit the form. Success! 

Notice that StateAdapt didn't require Steps 2-4. Check out the diff between NgRx and StateAdapt:

![Angular Reactive Forms diff between NgRx and StateAdapt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/chrk1rh5kwijcn43exah.png)

What is StateAdapt missing that makes it so minimal? Nothing. It has every layer NgRx has; each layer is just thinner.

Read more about StateAdapt [here](https://medium.com/weekly-webtips/introducing-stateadapt-reusable-reactive-state-management-9f0388f1850e) if you are interested.

## The Advanced Way
The simple method only puts one action type in Redux Devtools: 

![Action types are all the same](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0zvonb6z453oadand5nn.png)

You will probably want something more descriptive if your form is large. 

The basic pattern is established in the simple method above, so if you want to extend it, you will just need to create an action for each property of `FormState` and enhance the reducer to handle each action. If you have multiple form groups, you can use `PatchFormGroupValues` on each of them. If, however, you are defining an action for each form control, you need a new directive. Here is where you can use the `SetValue` directive:

```typescript
// set-value.directive.ts
import { Directive, Input } from "@angular/core";
import { NgControl } from "@angular/forms";

@Directive({
  selector: "[setValue]"
})
export class SetValueDirective {
  @Input()
  set setValue(val: any) {
    this.ngControl.control.setValue(val, { emitEvent: false });
  }

  constructor(private ngControl: NgControl) {}
}

```
It is used as you would imagine:
```html
<form>
  <input type="text" [formControl]="name" [setValue]="name$ | async" />
  <input type="number" [formControl]="age" [setValue]="age$ | async" />
</form>
```
In the component you would listen to the `valueChanges` of each form control and have a `using` call for each if you are using NgRx. I won't paste all the code here, but I do have [a working example in StackBlitz](https://stackblitz.com/edit/angular-reactive-forms-state-management?file=src%2Fapp%2Fform%2Fstate-adapt-advanced-form.component.ts) for StateAdapt. The result is a little more detail about what is happening:

![Action types are different](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbvz6coz9tmbj1zhuat2.png)

# Multiple Sources
## NgRx

`valueChanges` is just one possible source. We can plug in multiple sources in the same way. Rather than define them inside the `using`, we will define them outside and bundle them together with an RxJS `merge` so they all get subscriptions and dispatch to the store.

```typescript
  valueChanges$ = this.form.valueChanges.pipe(
    tap(values => this.store.dispatch(formValueChange(values)))
  );
  delayedFormState$ = timer(5000).pipe(
    tap(() =>
      this.store.dispatch(delayedFormStateRecieved({ name: "Delayed", age: 1 }))
    )
  );
  formValues$ = using(
    () => merge(this.valueChanges$, this.delayedFormState$).subscribe(),
    () => this.store.select(state => state.ngrx) // Wherever you put it in your state tree
  );
```

`delayedFormStateRecieved` is the same as `formValueChange` but with a different action type. I extended the reducer to handle both actions the same way:

```typescript
  on(
    formValueChange,
    delayedFormStateRecieved,
    (state, { type, ...update }) => ({ ...state, ...update })
  )
```

## StateAdapt

In StateAdapt everywhere you can plug in one source you can also plug in an array of sources. Both of our sources will emit values with the same interface and affect the same state change, so we will use an array here:

```typescript
  delayedFormState$ = timer(5000).pipe(
    map(() => ({ name: "Delayed", age: 1 })),
    toSource("[Form] Delayed Form State Received")
  );
  formValues$ = adapt(initialState, {
    sources: {
      update: [this.valueChanges$, this.delayedFormState$],
    },
  });
```

## Flexibility
This example of multiple sources illustrates the flexibility of functional reactive programming. You can plug in any source that emits the right kind of values without caring where they came from, and without the source caring how exactly you plan on using it. This means you can completely change its implementation without changing any of this code. 

The flexibility comes from the fact that all of the business logic for our form state is located together. This is much more flexible than the imperative style in jQuery, Angular Reactive Forms and others where each event source (or callback function) has to define the full extent of its own meaning to other areas of the app. Imperative programming is a violation of separation of concerns in favor of separation of code execution timing. The more asynchronous your application is, the more imperative programming violates separation of concerns. 

# Conclusion
When it is this easy to get Redux Devtools working for a form, I do not see many situations where you would not want to use it. Maybe NgRx is too much setup for a lot of forms, but if you add StateAdapt to your NgRx or NGXS project, you really only need to add 4 or so lines of code to enable Redux Devtools for a form. Plus, you have a much more reactive and declarative foundation for managing form state in the future!

You can control other attributes of form controls with directives, too, if you follow the same pattern. For example, I have a `ControlDisabled` directive in [my last blog post](https://dev.to/this-is-angular/isn-t-angular-reactive-forms-basically-just-jquery-22ol) you can use. 

If you are interested in learning more about StateAdapt, please [read my introduction post](https://medium.com/weekly-webtips/introducing-stateadapt-reusable-reactive-state-management-9f0388f1850e) or [visit the website](https://state-adapt.github.io/).