Join the social network of Tech Nerds, increase skill rank, get work, manage projects...
 
  • Model Driven Reactive Forms & Their Validation in Angular 4 Application

    • 0
    • 0
    • 0
    • 0
    • 0
    • 0
    • 3
    • 0
    • 1.53k
    Comment on it

    Model driven or Reactive forms provides reactive style of programming. These forms explicitly manages following of data between a non-UI data model (retrieved from server) and a UI-oriented form model capable of retaining the states and values of the HTML controls on screen. Reactive Forms find its usage in case of reactive patterns, testing and validation. Reactive forms belong to @angular/forms library within ReactiveFormsModule.

     

    In reactive forms component class will consist of a tree of angular form control objects which is then binded to component template native form control elements as a result all controls are always available and their value can be immediately updated if changes are made. These forms are synchronous. One might not think that if a tree of form control objects is created in component code then html for form will be automatically created for us instead html for form controls is to be created and then html form controls are linked to code in the component explicitly.

     

    Consider following example of Model Driven Form :-

     

    Create a Component "ModelFormComponent" using command "ng g c ModelForm" creating a folder with name "model-form" containing component, component template and component style files.

     

    Component code is in "model-form.component.ts" and component template is "model-form.component.html".

    Parent Component is "AppComponent".

     

    app.module.ts

     

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppComponent } from './app.component';
    import { ModelFormComponent } from './model-form/model-form.component';
    import { ReactiveFormsModule} from '@angular/forms'; //Include ReactiveFormsModule for using reactive forms
    
    @NgModule({
     declarations: [
      AppComponent,
      ModelFormComponent
     ],
     imports: [
      BrowserModule,
      ReactiveFormsModule,  
     ],
     providers: [],
     bootstrap: [AppComponent]
    })
    export class AppModule { }

     

    model-form.component.ts

     

    This file consist of form model. Instances of FormGroups and FormControls make up a form model in component. FormControls instances are nested inside FormGroups. FormGroups can also be nested inside another FormGroups.

     

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl } from '@angular/forms';
    
    @Component({
     selector: 'app-model-form',
     templateUrl: './model-form.component.html',
     styleUrls: ['./model-form.component.css']
    })
    export class ModelFormComponent implements OnInit {
     userform: FormGroup; //instance of FormGroup representing form
    
     constructor() { }
    
     ngOnInit() {
      // tree of form control objects
      this.userform = new FormGroup({
        name: new FormGroup({
          firstName: new FormControl(),  //instance of FormControl representing a control inside FormGroup
          lastName: new FormControl(),
        }),
        email: new FormControl(),
        password: new FormControl(),
      });
     }
    }

     

    model-form.component.html

     

    It links the component form model to the component form template. In aboce code "userform" form model is binded to <form> element using formGroup directive. This will only link form element to the userform model but does not link model form control to each form control in the template. This linking is performed explicitly via formControlName and formGroupName directives. Form values can be seen via <pre>{{userform.value | json}}</pre>, which are now binded to "userform" model in component. The template can be as follows :-

     

    <div class="container">
    <form [formGroup]="userform" novalidate>
      <fieldset formGroupName="name">
        <div class="form-group">
          <label>First Name</label>
          <input type="text" class="form-control" formControlName="firstName"/>
        </div>
    
        <div class="form-group">
          <label>Last Name</label>
          <input type="text" class="form-control" formControlName="lastName">
        </div>
      </fieldset>
    
      <div class="form-group">
        <label>Email</label>
        <input type="email" class="form-control" formControlName="email">
      </div>
    
      <div class="form-group">
        <label>Password</label>
        <input type="password" class="form-control" formControlName="password">
      </div>
      <div><pre>{{userform.value | json}}</pre></div>
    </form>
    </div>

     

    app.component.ts

     

    import { Component} from '@angular/core';
    
    @Component({
      selector: 'app-root',
      template: '<app-model-form></app-model-form>'
    })
    
     export class AppComponent{
    
      constructor () { }
     }

     

    index.html

     

    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>AngularDemo</title>
      <base href="/">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="icon" type="image/x-icon" href="favicon.ico">
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
    </head>
    <body>
      <app-root></app-root>
    </body>
    </html>

     

     

    When we run the application using "ng serve --open" following screen is obtained

     

     

    Changing values in input boxes also changes form model values

     

     

    Model Driven Form Validation

     

    Adding of validation checks in component form template through component form model. Input controls need to follow these validation checks if not followed then control is treated as invalid. There are pre-built validators like required, minlegth, maxlength and pattern which can be accessed via Validators module. ModelFormComponent consisted of a tree of form control objects. FormControl was used to create a control inside FormGroup. Validators can be applied to a control through FormControl constructor. The first parameter of a FormControl constructor is control initial value while the second parameter is for a single or list of validators applied to the control. 

     

    Example :

     

    Following files are changed :-

     

    model-form.component.ts 

     

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl,Validators } from '@angular/forms';
    
    @Component({
     selector: 'app-model-form',
     templateUrl: './model-form.component.html',
     styleUrls: ['./model-form.component.css']
    })
    export class ModelFormComponent implements OnInit {
     userform: FormGroup;
    
     constructor() { }
    
     ngOnInit() {
      this.userform = new FormGroup({
        name: new FormGroup({
          firstName: new FormControl("",Validators.required), //single validator
          lastName: new FormControl("",Validators.required),
        }),
        email: new FormControl("",[Validators.required,Validators.pattern("[^ @]*@[^ @]*")]), //list of validators for control
        password: new FormControl("",[Validators.required,Validators.minLength(8)]),
      });
     }
    }

     

    model-form.component.html

     

    
    <div class="container">
    <h3 class="jumbotron text-centre">User Form</h3>
    <form [formGroup]="userform" novalidate>
     <fieldset formGroupName="name">
      <div class="form-group" [ngClass]="{'has-danger': (userform.controls.name.controls.firstName.invalid && userform.controls.name.controls.firstName.touched)}">
       <label>First Name</label>
       <input type="text" class="form-control" formControlName="firstName" />
       <div class="form-control-feedback" *ngIf="userform.controls.name.controls.firstName.errors && userform.controls.name.controls.firstName.touched">
        <p class="text-danger" *ngIf="userform.controls.name.controls.firstName.errors.required">FirstName is required</p>
       </div>
      </div>
    
      <div class="form-group" [ngClass]="{'has-danger': (userform.controls.name.controls.lastName.invalid && userform.controls.name.controls.lastName.touched)}">
       <label>Last Name</label>
       <input type="text" class="form-control" formControlName="lastName">
       <div class="form-control-feedback" *ngIf="userform.controls.name.controls.lastName.errors && userform.controls.name.controls.lastName.touched">
        <p class="text-danger" *ngIf="userform.controls.name.controls.lastName.errors.required">LastName is required</p>
       </div>
      </div>
     </fieldset>
    
     <div class="form-group" [ngClass]="{'has-danger': (userform.controls.email.invalid && (userform.controls.email.touched || userform.controls.email.dirty))}">
      <label>Email</label>
      <input type="email" class="form-control" formControlName="email"/>
    
      <div class="form-control-feedback" *ngIf="userform.controls.email.errors && (userform.controls.email.dirty || userform.controls.email.touched)">
       <p class="text-danger" *ngIf="userform.controls.email.errors.required">Email is required</p>
       <p class="text-danger" *ngIf="userform.controls.email.errors.pattern">Not a valid pattern </p>
      </div> 
     </div>
    
     <div class="form-group" [ngClass]="{
       'has-danger': userform.controls.password.invalid && (userform.controls.password.dirty || userform.controls.password.touched)}">
      <label>Password</label>
      <input type="password" class="form-control" formControlName="password" >
    
      <div class="form-control-feedback" *ngIf="userform.controls.password.errors && (userform.controls.password.dirty || userform.controls.password.touched)">
       <p class="text-danger" *ngIf="userform.controls.password.errors.required">Password is required</p>
       <p class="text-danger" *ngIf="userform.controls.password.errors.minlength">Must be 8 characters long </p>
      </div> 
     </div>
    </form>
    </div>

     


    When application runs, model form errors can be seen when user try to edit(dirty), touch any field. Validation messages are displayed according to validators in input control of form model.

     


    From above template it can be seen that validation expression like

     

    userform.controls.name.controls.firstName.errors.required

     

    are becoming too large which is difficult to handle. In order to write shorter validation expression component file needs to be changed to following.

     

    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl,Validators } from '@angular/forms';
    
    @Component({
     selector: 'app-model-form',
     templateUrl: './model-form.component.html'
    })
    
    export class ModelFormComponent implements OnInit {
     userform: FormGroup;
     firstName: FormControl;
     lastName: FormControl;
     email: FormControl;
     password: FormControl;
    
     ngOnInit() {
      this.formControlsCreation();
      this.formCreation();
     }
    
     formControlsCreation() {
      this.firstName = new FormControl('', Validators.required);
      this.lastName = new FormControl('', Validators.required);
      this.email = new FormControl('', [Validators.required,Validators.pattern("[^ @]*@[^ @]*")]);
      this.password = new FormControl('', [Validators.required,Validators.minLength(8)]);
     }
    
     formCreation() {
      this.userform = new FormGroup({
       name: new FormGroup({
        firstName: this.firstName,
        lastName: this.lastName,
       }),
       email: this.email,
       password: this.password,
      });
     }
    }

     

    In above component local properties are created in component as a reflection of FormControls and thus directly binding these properties in component. These properties can be binded directly to component template without the need of going through top level instance of "userform" model. Now a template control for e.g: firstName can be rewritten as

     

      <div class="form-group" [ngClass]="{'has-danger': (firstName.invalid && firstName.touched)}">
          <label>First Name</label>
          <input type="text" class="form-control" formControlName="firstName" />
          <div class="form-control-feedback" *ngIf="firstName.errors && firstName.touched">
            <p class="text-danger" *ngIf="firstName.errors.required">FirstName is required</p>
          </div> 
        </div>

     

    Similarly other template controls can be changed thus shortning the validation expression

     

     

     

    Model Driven Reactive Forms & Their Validation in Angular 4 Application

 0 Comment(s)

Sign In
                           OR                           
                           OR                           
Register

Sign up using

                           OR                           
Forgot Password
Fill out the form below and instructions to reset your password will be emailed to you:
Reset Password
Fill out the form below and reset your password: