Ionic 3 and Forms - Part 2

In a previous post I walked you through the steps I followed when creating a reactive form using FormBuilder.

In this post, I'll show you how to add support for validation and error handling to forms created using FormBuilder.

Form validation

In order to provide a great user experience you need to validate user input for accuracy and completeness.

Built-in validators

Angular includes some built-in validators, like Validators.required and Validators.pattern().

To use the built-in validators we need to import the appropriate class:

import { FormBuilder, FormGroup, Validators } from '@angular/forms';

To assign a validator to a FormControl we simply include it in the 'key-value' pair that describes the FormControl:

this.credentialsForm = this.formBuilder.group({
  email: ['', Validators.required],
  password: ['', Validators.required]
});

The Validators.required validator ensures that a control has a non-empty value.

To assign multiple validators we use Validators.compose():

 email: [
   '', Validators.compose([
     Validators.pattern(regexValidators.email),
     Validators.required
   ])
 ],
 password: [
   '', Validators.compose([
     Validators.pattern(regexValidators.password),
     Validators.required
   ])
 ],

The Validators.pattern() validator ensures that a control matches a regex to its value.

Note: We also need to bind a FormControl to an <ion-input> using the [formControl] directive (which we did in the previous post).

Now that we have assigned Validators to the 'email' and 'password' FormControl's we can use them in our view.

We can check if our form is valid:

<button ion-button block color="secondary"
    [disabled]="!credentialsForm.valid"
    (click)="onSignIn()">
  Sign in
</button>

And if its not, we can disable the 'Sign in' button.

We can check if a specific field is valid:

<div *ngIf="!credentialsForm.controls.email.valid &&
    credentialsForm.controls.email.dirty"
    class="validator-error">
  Please enter a valid email.
</div>

And if its not, we can display an error message:

If you assign multiple validators to a FormControl, you can use the hasError() method to determine which validator failed:

<div *ngIf="credentialsForm.controls.password.hasError('pattern')">
  Passwords should be at least 8 characters long and contain one number,
  one character and one special character.
</div>

And display an error message:

Putting it all together

Here's how the SignInPage component look's now:

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

import { LoggerService } from '../../services/log4ts/logger.service';
import { regexValidators } from '../validators/validator';

@IonicPage()
@Component({
  selector: 'page-sign-in',
  templateUrl: 'sign-in.html'
})
export class SignInPage {

  credentialsForm: FormGroup;

  constructor(public navCtrl: NavController,
              public navParams: NavParams,
              private formBuilder: FormBuilder,
              private logger: LoggerService) {

    this.credentialsForm = this.formBuilder.group({

      email: [
        '', Validators.compose([
          Validators.pattern(regexValidators.email),
          Validators.required
        ])
      ],
      password: [
        '', Validators.compose([
          Validators.pattern(regexValidators.password),
          Validators.required
        ])
      ]
    });
  }

  onSignIn() {
  
    if (this.credentialsForm.valid) {
      this.logger.info('Email: ' +
        this.credentialsForm.controls['email'].value);
      this.logger.info('Password: ' +
        this.credentialsForm.controls['password'].value);
    }
  }

  onForgotPassword() {
    this.logger.info('SignInPage: onForgotPassword()');
  }
}

And the view template:

<ion-header no-border>
</ion-header>

<ion-content no-bounce padding>

  <ion-row class="app-icon-container">
    <ion-col text-center>
      <ion-icon name="ionic" class="app-icon-zoom"></ion-icon>
    </ion-col>
  </ion-row>

  <form [formGroup]="credentialsForm">

    <ion-item>
      <ion-label floating>Email</ion-label>
      <ion-input [formControl]="credentialsForm.controls['email']"
          type="email"></ion-input>
    </ion-item>

    <div *ngIf="!credentialsForm.controls.email.valid &&
        credentialsForm.controls.email.dirty"
        class="validator-error">
      Please enter a valid email.
    </div>

    <ion-item>
      <ion-label floating>Password</ion-label>
      <ion-input [formControl]="credentialsForm.controls['password']"
          type="password"></ion-input>
    </ion-item>

    <div *ngIf="!credentialsForm.controls.password.valid &&
        credentialsForm.controls.password.dirty"
        class="validator-error">
      Please enter a valid password.
    </div>>

    <ion-row class="sign-in-button-container">
      <ion-col text-center>
        <button ion-button block color="secondary"
            [disabled]="!credentialsForm.valid" (click)="onSignIn()">
          Sign in
        </button>
      </ion-col>
    </ion-row>

    <ion-row>
      <ion-col text-center>
        <button ion-button clear color="light"
            (click)="onForgotPassword()">
          Forgot your password?
        </button>
      </ion-col>
    </ion-row>

  </form>

</ion-content>

Let's try running the application to make sure it’s working as expected:

ionic serve --platform=ios

You should see output like:

What's Next

In the next post, I'll show you how to add support for animation:

Source Code:
Resources:
Additional Resources: