Tuesday, July 12, 2016

Ionic 2: FormBuilder and Validator

In the article "Learn Ionic 2 with Authentication by JWT", you may know how to make a login page and navigate between pages after login.

In this article, we are going to improve the Signup form of Login page with FormBuilder and Validation which are strong weapons of Angular 2.

1. Add an URL to check existing user in the authentication server
Open the file user-routes.js in the authentication server, add new function to check a username if existing:
app.post('/check', function(req, res) {
  var userScheme = getUserScheme(req);
  if (!userScheme.username) {
    return res.status(400).send("You must send a username");
  }

  if (_.find(users, userScheme.userSearch)) {
   return res.status(400).send("A user with that username already exists");
  }

  res.status(201).send({username: "OK"});
});


Then start the server by using the command:
#node server.js

2. Mofidy file app/providers/auth-service/auth-service.ts
Open this file and add new member and method (function) to check a user as the following:
CHECK_URL = "http://localhost:3001/check";
check(user) {
        return new Promise((resolve, reject) => {
            this.http.post(this.CHECK_URL, JSON.stringify({username: user}), { headers: this.contentHeader })
                .map(res => res.json())
                .subscribe(
                    data => {
                        resolve(data)
                    },
                    err => {
                        reject(err)
                    }
                );
        });
}


3. Add new file app/validators/password.ts
It is used to validate the password input in Signup form. This file has content as below:
import {Control} from '@angular/common';
export class PasswordValidator {
    static checkPassword(control: Control) {
        // password must be between 4 and 10 characters and have at least 1 number
        if (control.value.match(/^(?=.*[0-9])[a-zA-Z0-9!@#$%^&*]{4,10}$/)) {
            return null;
        } else {
            return { 'invalidPassword': true };
        }
    }
}


4. Modify app/pages/login/login.ts
This file is modified as below:
import {Page, NavController} from 'ionic-angular';
import {FormBuilder, ControlGroup, Validators } from '@angular/common';
import {AuthService} from '../../providers/auth-service/auth-service';
import {WorkPage} from '../work/work';

import {PasswordValidator} from  '../../validators/password';
@Page({
  templateUrl: 'build/pages/login/login.html',
})
export class LoginPage {
    authType: string = "login";
    usernameIsValid: boolean = false;
   
   
constructor(private auth: AuthService, private nav: NavController, private formBuilder: FormBuilder) {
        this.signupCreds = formBuilder.group({
          'username': ['', Validators.compose([Validators.required, Validators.pattern('[a-zA-Z]*')])],
          'password': ['', PasswordValidator.checkPassword],
          'email': ['', Validators.required]
        });
    }


    checkUsername(username){
        if(username.length < 4) { //username must have at least 4 characters
          this.usernameIsValid = false;
          return;
        }
        this.auth.check(username.toLowerCase()).then(
          (success) => {
            this.usernameIsValid = true;
          },
          (err) => {
            this.usernameIsValid = false;
          }
        );
    }

   
    login(credentials) {
        this.auth.login(credentials).then(
          (success) => {
            this.nav.setRoot(WorkPage);
          },
          (err) => console.log(err)
        );
    }
   
    signup(credentials) {
        this.auth.signup(credentials).then(
          (success) => {
            this.nav.setRoot(WorkPage);
          },
          (err) => console.log(err)
        );
    }
}


The new BLUE codes are added / modified. Pay your attention on the constructor which I use FormBuilder to created signupCreds form and bind its controls to Validators. As you can see the password control use PasswordValidator.checkPassword to validate input password.

4. Modify app/pages/login/login.html
Let modify the signup form as the following:
<form *ngSwitchWhen="'signup'" [ngFormModel]="signupCreds" (ngSubmit)="signup(signupCreds.value)">
        <ion-item>
          <ion-label>Username</ion-label>
          <ion-input (change)="checkUsername(signupCreds.controls.username.value)" type="text" ngControl="username"></ion-input>
        </ion-item>
        <p *ngIf="signupCreds.controls.username.hasError('required') && signupCreds.controls.username.touched" danger>Username is required</p>
        <p *ngIf="signupCreds.controls.username.touched && !usernameIsValid" danger>User is invalid or taken</p>
        <p *ngIf="signupCreds.controls.username.touched && usernameIsValid" secondary> User is OK</p>
       
        <ion-item>
          <ion-label>Password</ion-label>
          <ion-input type="password" ngControl="password"></ion-input>
        </ion-item>
        <p *ngIf="!signupCreds.controls.password.valid && signupCreds.controls.password.touched" danger>Password is not meet standards</p>
        <p *ngIf="signupCreds.controls.password.valid && signupCreds.controls.username.touched" secondary> Password is good</p>


        <ion-item>
          <ion-label>Email</ion-label>
          <ion-input type="email" placeholder="Your email" ngControl="email"></ion-input>
        </ion-item>
        <p *ngIf="signupCreds.controls.email.hasError('required') && signupCreds.controls.email.touched" danger>Email is required</p>
       
        <div padding>
          <button block [disabled]="!usernameIsValid || !signupCreds.controls.password.valid || !signupCreds.controls.email.valid" type="submit">Signup</button>
        </div>
</form>


While email uses a standard Validator and password uses a customized Validator, username uses event change of the control to validate an input username. This will help to reduce number of calls to the authentication server to check the input username. If not, it will call the authentication server when keying every character in Username box.

Beside that, in this new form, you can not click Signup button if any control is invalid. Each control will have a message to inform you it is OK or not.

Now you can run ionic serve then try this new signup form. Here is my screen:

Hope this article can help you on developing your app on Ionic 2.
Happy coding! Cheers.


Subscribe to RSS Feed Follow me on Twitter!