Ionic Formbuilder

In this example of Ionic Form, we are looking only at how validation can be applied and not on data storage at this moment. In ionic, Angular 2 introduces the FormBuilder, is a helper class. Using the FormBuilder we can programmatically build our forms and use that logic
to control the input fields and form the state in our templates.  The FormBuilder, a helper class that allows developers to take following advantage:

  • Implementing validation rules for input field
  • Listening for value changes on input fields
  • Updating the form UI to help guide user behavior (I.e. disabling buttons where required input fields have not been completed & displaying error messages)

Screenshot of our form apps.

We need to follow these step to complete are apps.

Step 1: ionic start advancedForm blank –v2
cd advancedForm
npm install // Install additional node modules
ionic g provider Validator

Step 2: We can create some validation rules to use with the FormBuilder service. Open the advancedForm/src/providers/validator/validator.ts and add the following code.

import { Injectable } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class Validator {
  constructor(private http: Http){}


  emailValid(control: FormControl){
    return new Promise(resolve => {
      let emailPattern = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
    
      if(!emailPattern.test(control.value)){
        resolve({ InvalidEmail : true });
      }
      resolve(null);
      });
    }
        
    nameValid(control: FormControl){
        return new Promise(resolve =>{
          let pattern = /[0-9]/;
          if(pattern.test(control.value)){
            resolve({ InvalidName : true });
          }
          resolve(null);
      });
    }
}

In our service, we have to import the Angular Forms FormControl class which we will use in our subsequent methods to obtain the value from the input field that we are looking to validate. We had to define two methods nameValid and emailValid in validator.ts.  The validator.ts is a custom class where both methods use promises to return the results of tests that we perform on the supplied input field value using regular expressions.

Based on regular expression result we will display error when regular expression fails, we return JSON object that will use to display error messages in the HTML template, otherwise, we return null object indicating validation successful. We can use this validation service to plug into our home.ts class using FormBuilder service.

Step 3: Add the following code in app/home/home.ts

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { FormBuilder, FormGroup, Validators} from '@angular/forms';

import { Validator } from '../../providers/validator';
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})

export class HomePage {
  private form : FormGroup;
  private name : any;
  constructor(private navCtrl : NavController, private fb : FormBuilder, private val : Validator ){
    this.form = fb.group({
      'name' : ['', Validators.required, val.nameValid],
      'email' : ['', Validators.required, val.emailValid],
      'platform' : ['', Validators.required]
    });

    this.name = this.form.controls['name'];
    this.name.valueChanges.subscribe((value: string) =>{
        console.log(`Entered name is ${value}`);
      });
    }
    saveDetails(value) {
      console.dir(value);
    }
}

In the first highlight on the code, we are importing 3 angular form service FormBuilder, FormGroup, Validators, and our custom validator.ts service provider.

We are using these services to manage the input field logic and validation functionality for the page template. A FormGroup allows us to manage more than one input field (which is quite handy if we have forms with multiple fields that we need to work with) allowing our app to programmatically determine the state of each specified input field (I.e. whether it is valid, has changed etc).

The FormBuilder service will be used as a utility wrapper to help manage the
FormControl and FormGroup services. Validators, as the name implies, provide the ability to validate input fields.

This FormGroup contains 3 FormControls (rendered as key/value pairs) which are used to target each individual input field in our form.
Each FormControl is assigned its respective Validators, where for the name and email controls we assign our custom validation methods from the Validator service.

We also listen to value changes in the name input field using the valueChanges method of the FormControl object (which gives us access to the EventEmitter, an Observable that allows us to track custom events in our applications).

At last in home.ts we implement the saveDetails method which will be used to retrieve the submitted form data. In saveDetails method we have console.dir(value).

Note: Specifically, console.log gives special treatment to DOM elements, whereas console.dir does not.  The console.dir  is often useful when trying to see the full representation of the DOM JS object.  Displays an interactive list of the properties of the specified JavaScript object.  The output is presented as a hierarchical listing with disclosure triangles that let you see the contents of child objects.

Step 4: Add the following code in app/home/home.html

<ion-header>
  <ion-navbar><ion-title>Your Personal Details</ion-title></ion-navbar>
</ion-header>
<ion-content padding>
  <form [formGroup]="form" (ngSubmit)="saveDetails(form.value)">
    <ion-list>
      <ion-item margin-bottom>
        <ion-label>Your Name</ion-label>
        <ion-input type="text" [formControl]="form.controls['name']"></ion-input>
      </ion-item>
      
      <ion-item margin-bottom>
        <ion-label>Your Email address</ion-label>
        <ion-input type="email" [formControl]="form.controls['email']"></ion-input>
      </ion-item>

      <ion-item margin-bottom>
        <ion-label>Favourite Platform</ion-label>
          <ion-select [formControl]="form.controls['platform']">
            <ion-option value="Android">Android</ion-option>
            <ion-option value="iOS">iOS</ion-option>
            <ion-option value="WP">Windows Phone</ion-option>
            <ion-option value="Other">Other</ion-option>
            </ion-select>
      </ion-item>
      <button ion-button color="primary" text-center block [disabled]="!form.valid">Save Details</button>
    </ion-list>
  </form>
  <div *ngIf="form.controls['name'].dirty && !form.controls['name'].valid">
    <p *ngIf="form.controls['name'].errors.InvalidName">Your name cannot contain any numbers.</p>
  </div>
  <div *ngIf="form.controls['email'].dirty && !form.controls['email'].valid">
    <p *ngIf="form.controls['email'].errors.InvalidEmail">You must enter a valid e-mail address</p>
  </div>
</ion-content>

Note: In the home.html we have the formGroup property on our HTML form to create a link with the FormBuilder logic in the component class and the template input fields. We can implement our HTML FormControl with either of the following syntaxes:

We can implement our HTML FormControl with either of the following syntaxes:
[formControl] = “form.controls[‘name’]” OR formControlName=”name”
Either way is perfectly valid.

The formGroup property is set to a value of form which matches the
same property name used in the component class (where we created a FormGroup using the FormBuilder helper).
Each input field has a formControl assigned to it whose value is mapped to the specified FormControl object in the component class (this relationship allows the FormBuilder helper to retrieve each field’s input value).

The ability to submit the form is determined through the FormBuilder object
assessing the state of each input field, whether it has data and has successfully passed all applied validation criteria – if not the submit button state remains disabled.

The FormBuilder component logic listens for data entered, determines its validity and whether any errors have occurred (and, if so, displays these under the form).

Step 5: Add last we have to add our custom validator service in provider option in app.module.ts

Note: The underlying Angular 2 framework detects the state of the input field and applies its own specific classes to reflect those states:

  • ng-dirty (Input field has been interacted with)
  • ng-touched (Input field has received focus)
  • ng-valid (Data entry has passed validation)
  • ng-invalid (Data entry has not passed validation)