Implement Cross Component Validation in Angular Forms using Akita
Tags: angular, akita, forms
Published: 2019-07-08 16:02:29

---
title: Implement Cross Component Validation in Angular Forms using Akita
published: true
description: cross component validation in Angular forms with Akita
tags: angular, akita, forms
---

In this article, I want to show you how you can use `AkitaFormsManager` plugin and achieve the same result (and more) described in <a href=“https://rangle.io/blog/ngrx-forms/”>this</a> ngrx article. 

## The Plugin Abilities

- Persist Forms - Automatically saves the current control value and update the form value according to the value in the store when the user navigates back to the form.

- Provides an API so we can query a form’s values and properties from anywhere. This can be useful for things like multi-step forms, cross-component validation, and more.

### Installation
`npm i @datorama/akita-ng-forms-manager`

### Create the Routes
We will have two pages, one is `PersonPage` and the second is `ConfigPage`:

```ts
const routes: Routes = [
  {
    path: '',
    component: PersonComponent,
    pathMatch: 'full'
  },
  {
    path: 'config',
    component: ConfigComponent
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}
```

### The Person Component
Now let's create the `PersonComponent` form component:

```ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { AkitaNgFormsManage } from '@datorama/akita-ng-forms-manager';

@Component({
  selector: 'person-form',
})
export class HomeComponent implements OnInit, OnDestroy {
  person = new FormGroup({
    firstName: new FormControl(),
    lastName: new FormControl(),
    age: new FormControl(21, Validators.required)
  });

  constructor(private formsManager: AkitaNgFormsManager) {}

  ngOnInit() {
    this.formsManager.upsert('person', this.person);
  }

  ngOnDestroy() {
    this.formsManager.unsubscribe();
  }
}
```

and the template:
```html
<form [formGroup]="person">
  <input formControlName="firstName" placeholder="First Name" />
  <input formControlName="lastName" placeholder="Last Name" />
  <input
    formControlName="age"
    placeholder="Age"
    type="number"
    [ngStyle]="person.invalid && { border: '1px solid red' }"
  />
</form>
```

My favorite part of this plugin is that we still work with the existing Angular API to create our form. 

We created a basic reactive form group which contains the person's details. We are injecting the `AkitaNgFormsManager` and calling the `upsert` method, giving it the form name and the `formGroup`.

From that point on, `AkitaNgFormsManager` will track the form value changes and update the store accordingly. It also persists the form state when the user navigates back to the form. 

We can also see everything in the dev-tools:

![](https://thepracticaldev.s3.amazonaws.com/i/r5wf5sjeomim92afdedg.gif)

### The Config Component

Now, let's create the `ConfigComponent`:

```ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { AkitaNgFormsManager } from '@datorama/akita-ng-forms-manager';

@Component({
  selector: 'app-config',
  templateUrl: './config.component.html',
  styleUrls: ['./config.component.css']
})
export class ConfigComponent implements OnInit, OnDestroy {
  config = new FormGroup({
    minAge: new FormControl(21)
  });

  constructor(private formsManager: AkitaNgFormsManager) {}

  ngOnInit() {
    this.formsManager.upsert('config', this.config);
  }

  ngOnDestroy() {
    this.formsManager.unsubscribe();
  }
}
```

Same as before, we create a `FormGroup` which contains one `FormControl` - the minimum age that the user is <b>required</b> to type at the `PersonComponent` form. We also register the form with the `AkitaNgFormsManager` so we can query it from any other component in our application.

### Cross Component Validation

Now, let's go back to the `PersonComponent` and see how we can make the `age` control validation dynamic based on the `minAge` control value:

```ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { AkitaNgFormsManager, setValidators } from '@datorama/akita-ng-forms-manager';

export class HomeComponent implements OnInit, OnDestroy {
  person = new FormGroup({
    firstName: new FormControl(),
    lastName: new FormControl(),
    age: new FormControl(21, Validators.required)
  });

  dispose;

  constructor(private formsManager: AkitaNgFormsManager) {}

  ngOnInit() {
    this.formsManager.upsert('person', this.person);

    this.dispose = this.formsManager.selectValue<number>('config', 'minAge')
      .subscribe(minAge => {
        setValidators(this.person.get('age'), [Validators.min(minAge)]);
      });
  }

  ngOnDestroy() {
    this.formsManager.unsubscribe();
    this.dispose.unsubscribe();
  }
}
```

We can query any form that is registered in `AkitaNgFormsManager`. In our case, we use the `selectValue` method passing the form name and the form field we need. Note that we also can infer the value using a generic. 

Now the only thing that is left is to call the `setValidators` method (provides by the plugin) passing the control and a `Validators` array.


![](https://thepracticaldev.s3.amazonaws.com/i/hvv1xfn7opp68ouslve1.gif)

To preserve existing validators, we can do:

```ts
this.dispose = this.formsManager.selectValue<number>('config', 'minAge')
 .subscribe(minAge => {
  const age = this.person.get('age');
  const validators = age.validator;
  setValidators(age, [...validators, Validators.min(minAge)]);
});
```


Read more about the plugin in the <a href="https://netbasal.gitbook.io/akita/angular-plugins/angular-forms-manager">docs</a>.