DEV Community

Ajay Ojha
Ajay Ojha

Posted on

New Way to Validate the Angular Reactive Form

cross posted to medium.com

Here, We will discuss model object based reactive form validation, which overcomes the current challenges of angular reactive forms.

High-Level Challenges?

When you are working on a more data-oriented advanced reactive form, you’ll notice that you are validating much more than just your inputs, some of the validators and few common things are not easily available in the angular framework. Then you design the custom validation and subscribe to the value change of FormControl to achieve the functional needs of your application. This introduces additional noise in the code to do the same thing again and again in most of the cases.

Some of the current reactive form validation challenges are jotted down.

  • As such, there is no plain model object where we can define properties based upon respective data types and directly map it with reactive form.
  • We can’t map the model object value directly with validations to the reactive form while creating a FormGroup. *Manually configure the validators on every respective property(FormControl).
  • If there is a specific need to perform some other operation based on respective FormControl value change then we have to subscribe to the respective value change of FormControl and do the further activity, which is a bit difficult to manage in large forms.
  • Writing too much code to manage cross-field validation and conditional validation, if there are more than one validator on respective FormControl.

To work on the new approach and overcome the current challenges, We will use @rxweb/reactive-form-validators for angular reactive form.

Why @rxweb/reactive-form-validators?

If I am talking about typescript, the typescript programming language highly relies on Object-Oriented Programming practices, which makes more power to write clean code. This is the main reason to design this framework to provide a model object based reactive form. Because this gives you more flexibility when the complexity of application form increase and application grows at that time you don’t bog down the cumbersome activity of assembling the validators and other value subscriptions.

Now, Let’s create the Model Object-Based Reactive Form with Decorator based validation.

What is decorator based validation?

Let’s say, you want few validations on userName field like alpha, minLength and required. Then you can set those validations in the form of validation decorator on the userName property of your User model. The @rxweb/reactive-form-validators will add all configured validation decorators while creating a FormControl of the userName property. The code example as below:

export class User {
    @alpha()
    @minLength({ value: 5 })
    @required()
    userName: string;
}
Enter fullscreen mode Exit fullscreen mode

Functional Requirement

Meet Mike, Mike is working on social media application and wants to build the user registration form. Registration form mockup, functional need, validation rules are as below:

Mockup of the user registration form.

  • All red marked fields are required.
  • Confirm field only valid when password and confirm field values are same.
  • The fullName property is read-only and it’s the combined value of firstName and lastName, Whenever the user enters the value in respective FormControl then fullName updated value will reflect immediately on UI.

Now, Mike starts to build the model object-based reactive form.

Before developing the form Mike installs the package of @rxweb/reactive-form-validators. Package installation command as below:

npm install @rxweb/reactive-form-validators

Once the package is installed, Mike registers the ‘RxReactiveFormsModule’ in the root module to avail the services of this framework.
Here is the below code example:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule,ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

import {  RxReactiveFormsModule } from "@rxweb/reactive-form-validators"
@NgModule({
  imports:      [ BrowserModule, FormsModule,ReactiveFormsModule,RxReactiveFormsModule ],
  declarations: [AppComponent],
  bootstrap:    [ AppComponent]
})
export class AppModule { }
Enter fullscreen mode Exit fullscreen mode

Once the ‘RxReactiveFormsModule’ is registered, Mike creates a User model with respective properties as shown in the mockup and also applies validation decorators on the properties as per the registration form need.

export class User {
    @required()
    firstName: string;

    @required()
    lastName: string;

    @required()
    userName: string;

    @required()
    password: string;
}
Enter fullscreen mode Exit fullscreen mode

As per the above code, Mike has applied the required validation decorator on respective fields but missed the scenario of second and third.

Now let’s see, how to achieve the second and third scenario.

As per the second scenario is to compare the value of two controls, to fulfill the need. Mike is using compare decorator which compares the cross field FormControl value.
The only property of confirmPassword and applied validation decorator as below:

@compare({fieldName:'password'})
confirmPassword:string;
Enter fullscreen mode Exit fullscreen mode

Now to achieve the third scenario, Mike will not subscribe the ValueChanges property and set the value in the fullName field when the value will change in firstName or lastName. Mike does some corrections in already defined firstName and lastName property. Mike uses getter/setter typescript feature on firstName and lastName. See below are the changes:

    private _firstName: string = '';
    private _lastName: string = '';

    fullName: string;

    @required()
    set firstName(value: string) {
        this._firstName = value;
        this.setFullName();
    }

    get firstName(): string {
        return this._firstName;
    }

    @required()
    set lastName(value: string) {
        this._lastName = value;
        this.setFullName();
    }

    get lastName(): string{
       return this._lastName;
    }

    setFullName() {
        this.fullName = `${this.firstName} ${this.lastName}`;
    }
Enter fullscreen mode Exit fullscreen mode

As Mike has applied the getter/setter on firstName and lastName property and created a method of setFullName for updating the value fullName. Now one question arises:

Why Mike has used getter/setter property?

Whenever the user enters a value in the firstName or lastName FormControl at that time firstName and lastName property setter function will call with the latest value. Inside the setter function, Mike has set the value in private property of the respective public property. This gives better flexibility in the application as the same model will use in multiple components. Mike doesn’t need to write the same code in multiple components and also doesn’t need to subscribe to the ValueChanges. See the below image :

Now let’s see, the complete User model.

import { required,compare } from "@rxweb/reactive-form-validators";

export class User {
    private _firstName: string = '';
    private _lastName: string = '';

    fullName: string;

    @required()
    set firstName(value: string) {
        this._firstName = value;
        this.setFullName();
    }

    get firstName(): string {
        return this._firstName;
    }

    @required()
    set lastName(value: string) {
        this._lastName = value;
        this.setFullName();
    }

    get lastName(): string{
       return this._lastName;
    }

    @required()
    userName: string;

    @required()
    password: string;

    @compare({fieldName:'password'})
    confirmPassword:string;

    private setFullName() {
        this.fullName = `${this.firstName} ${this.lastName}`;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, Mike creates a FormGroup from User model object in the component. Mike uses the ‘formGroup’ method of RxFormBuilder for creating a FormGroup.
Here is the code :

import { Component, OnInit } from '@angular/core';
import { FormGroup } from "@angular/forms"

import { RxFormBuilder } from '@rxweb/reactive-form-validators';

import { User } from '../user.model';

@Component({
    selector: 'app-user-add',
    templateUrl: './user-add.component.html'
})
export class UserAddComponent implements OnInit {

    userFormGroup: FormGroup
    user:User;
    constructor(
        private formBuilder: RxFormBuilder
    ) { }

    ngOnInit() {
        this.user = new User();
        this.userFormGroup = this.formBuilder.formGroup(this.user);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, Mike writes the HTML as per angular reactive form standards. But the fullName property is not a FormControl. Mike has used user object for showing fullName value. See the below code:

<div class="form-group">
    <label>Full Name : {{user.fullName}}</label>
</div>
Enter fullscreen mode Exit fullscreen mode

Here is the complete HTML code:

<h1 class="bd-title" id="content">Model Object based reactive form</h1>
<br/>
<form *ngIf="userFormGroup" [formGroup]="userFormGroup">

<div class="form-group">
    <input type="text" formControlName="firstName" placeholder="First Name" class="form-control"  />

</div>
<div class="form-group">
    <input placeholder="Last Name" type="text" formControlName="lastName" class="form-control"  />

</div>
<div class="form-group">
    <input placeholder="UserName" type="text" formControlName="userName" class="form-control"  />

</div>
<div class="form-group">
    <input placeholder="Password" type="text" formControlName="password" class="form-control"  />

</div>
<div class="form-group">
    <input placeholder="Confirm" type="text" formControlName="confirmPassword" class="form-control"  />

</div>
  <div class="form-group">
    <label>Full Name : {{user.fullName}}</label>
  </div>
<button [disabled]="!userFormGroup.valid" class="btn btn-primary">Submit</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Please checkout the complete working example code on stackblitz

One question may arise in your mind, Why fullName FormControl is not created. As this is also public property as like other properties (firstName, lastName, etc…). I will give this answer in my next article with more details.

If you like this approach then some other question also arise, like:

  • How nested FormGroup will create?
  • How nested FormArray will create?
  • How to update the value of particular FormControl with this approach?
  • How can I set my custom validation with this approach?
  • n….nnn…..nnn number of questions?

All can be possible with model object reactive form validation. I will share some highlights in my next coming articles.

Hope you like the new validation approach in a reactive form. If you have any suggestion on this approach, please write your comment below.

Top comments (1)

Collapse
 
rembou1 profile image
remi bourgarel

It should have been like this from the beggining !