Here, We will discuss model object based reactive form validation, which overcomes the current challenges of angular reactive forms.
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.
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;
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:
- 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
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;
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:
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`;
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`;
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);
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:
Here is the complete HTML code:
Model Object based reactive form