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.


Sunday, July 10, 2016

Ionic: How to solve CORS issue

What is CORS?
CORS = Cross Origin Resource Sharing

When developing an Ionic app, do you ever get the error: No 'Access-Control-Allow-Origin' header is present on the requested resource

It is caused by an Ajax call (XMLHttpRequest) to another site when you run your app in the browser by the command ionic serve or ionic run. It won't occur when your app is built and load to platform (iOS/Android).

To solve this problem and get your app run smoothly on the browser like on the platform iOS/Android, let use Chrome and install this extension: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi




Instead of uploading your app to the platform iOS / Android which taking your time. Using above Chrome extension will help you develop and test your app faster.

Happy coding! Welcome any command.
Subscribe to RSS Feed Follow me on Twitter!