Ionic [2 & 3] | Reactive Forms Creation and Validation Example

Written by on July 1, 2019

Ionic [2 & 3] | Reactive Forms Creation and Validation Example

The original version of this article was written two years ago, which is way too old for what Ionic can provide today. I never intended to update this article, why spend so much time managing old content when Ionic/Angular is changing so fast, sometimes not even in a good way. To some point, I’m regretting that decision; not completely but enough that it’s bothering me on a daily basis.

So, in that light let me introduce you the next iteration of this article. Hopefully, it will serve you better than the old one.

In my previous (it was so long ago) article, I demonstrated how to share data between different Ionic [2|3] pages or components. This specific tutorial will show you how we can work and build application forms including data validations.

Subjectively, I can’t remember anything in Ionic 1 as annoying as form handling and validations. This is mostly due to Angular shortcomings; I don’t want to use this comment as a hit on Ionic 1.

In those dark ages, we had to use ngModel to map our form data model. Thankfully, now we’re doing it much more explicitly; we’re probably going to use more code than before, but, at the same time, it will be much easier to understand. One last thing, don’t forget, we no longer need to play with frustrating ngModel and suffer any scope of data problems.

The advent of Angular 2 has brought us a quite bit different way of form handling, especially compared to what we had with Angular 1. I will show you how to translate these changes to Ionic 2 application, but, at the same time, you can count this as Angular 2|4 article.

Note: If this tutorial was helpful, you need further clarification, something is not working or you have a request for another Ionic post? Furthermore, leave me a comment below if you don't like something about this blog, if something is bugging you, don't like how I'm doing stuff here. Feel free to comment below, subscribe to my blog, mail me to dragan.gaic@gmail.com. Thanks and have a nice day!

Table of Contents

Is Ionic (2 or 3) causing you problems? Are you struggling with your application? Trust me, I was in the same situation as you. If you require more information beyond the subject of this article, you will probably find it in a list below. Take a look; if there’s a topic not covered here, leave me a comment, and I will do my best to cover it.

[spoiler title=”Click here if you want to see a list of topics covering Ionic (2|3) framework”]

Introduction to Object Oriented JavaScript

Ionic 2 Tutorials


[/spoiler]

Preparations

As always, make sure you have everything set up for Ionic [2|3] development. One last thing, as this framework is now known only as Ionic, further down the line I will refer to it as such; I will not use any version numbers. I will simply refer to it as Ionic.

To continue working on this tutorial you should have these:

  • Android Environment (or iOS if you’re working on a MacOS)
  • nodeJS
  • Ionic
  • Cordova

If you don’t have a prior Ionic installation find more information here: Ionic [2|3] | Installation Guide.

1. Update Ionic CLI

If possible you should have the latest nodeJS version, without it, you’ll not be able to appropriately install/update Cordova and Ionic. Worst case scenario, use any recent version possible.

On the other hand, If you have already worked with Ionic/Cordova, make sure they’re up to date; older versions may not work with this tutorial:

npm install -g ionic cordova

or to do a simple update:

npm update -g ionic cordova

2. Create A New Project

ionic start IonicFormValidation blank
cd IonicFormValidation 

If you prefer a working example you will find it at the end of this article; if not, keep reading.

Warning: As some of you don't have a prior Ionic CLI experience, from this point and on, every time I tell you to execute something, do that inside an example project folder.

3. Add Required Platform

Add Android platform:

ionic cordova platform add android

MacOS users can also add iOS platform:

ionic cordova platform add ios

Source Walkthrough

I will make this tutorial as easy as possible; if you want to see a different example or case just leave me a comment. For now, our example will need only two pages. One page will cover the authentication form while another one will hold a simple home page where we will end up after successful authentification.

The ionic blank app, as by default, comes with the pre-existing Homepage; we will need to add the second authorization page:

ionic generate page auth

This will generate a new folder called auth (inside pages folder) including our new AuthPage page. This process is done automatically, an AuthPage name was formed using page name we provided with ionic generate. You can also do this manually but this way is much faster and error free.

Just be careful, unlike Angular CLI generate, Ionic CLI generate will not integrate new page with rest of the code, we will need to do that manually.

So, as previously mentioned, before we can proceed any further, let’s include auth page into app.module.ts file.:

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { AuthPage } from '../pages/auth/auth';

@NgModule({
  declarations: [
    MyApp,
    HomePage,
    AuthPage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage,
    AuthPage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

At this point, we have a simple app with two distinct pages. Let us proceed to app.component.ts file.

As we are offering authentication we first need to decide which page will show first. We will display auth page if an application was started for the first time, if authentication was unsuccessful or if logout function was triggered on the home page. On the other side, we will display homepage if authentication was successful, or if the original authentication was successful and login data was stored in a localstorage:

  checkPreviousAuthorization(): void { 
    if((window.localStorage.getItem('username') === "undefined" || window.localStorage.getItem('username') === null) && 
       (window.localStorage.getItem('password') === "undefined" || window.localStorage.getItem('password') === null)) {
      this.rootPage = AuthPage;
    } else {
      this.rootPage = HomePage;
    }
  }

Also, don’t forget to include AuthPage, as HomePage is the only one included by default:

import { AuthPage } from '../pages/auth/auth';

Now let’s open auth.ts page as we will spend most of our time there.

To be able to work with Ionic forms we need to import several Angular2 services, directives and objects:

import { FormBuilder, FormGroup, Validators, AbstractControl } from '@angular/forms';
  • While we can build forms using ngForm and ngControl, a more flexible way is using FormBuilder. It’s just a helper service (class) that helps us build forms using Control and ControlGroup objects.
  • A single Control object represents a single form item. At the same time, ControlGroup is a wrapper interface around a collection of Control objects.
  • Validators is a service (module) we use for validation purposes.
  • AbstractControl is an abstract class and a common ancestor of Control and ControlGroup.

For our example, we will use two distinctive input boxes and one submit button. For now, just disregard Angular2 syntax.

<ion-content padding>
    <form [formGroup]="authForm" (ngSubmit)="onSubmit(authForm.value)">
        <ion-item>
            <ion-label floating>Username</ion-label>
            <ion-input formControlName="username" type="text"></ion-input>
        </ion-item>
 
        <ion-item>
            <ion-label floating>Password</ion-label>
            <ion-input formControlName="password" type="password"></ion-input>
        </ion-item>

        <button ion-button full color="primary" [disabled]="!authForm.valid" style="margin-top: 20px;" type="submit">Authorize</button>        
    </form>
</ion-content>

Take a careful look at each input field, you’ll notice a special keyword called formControlName:

<ion-input formControlName="username" type="text"></ion-input>

This keyword defines a Control object (a single form field).

We will not need to manually change these values in our code but if you want to do that you can do it like this (in auth.ts):

username: AbstractControl;
password: AbstractControl;		

Both username and password are defined as AbstractControltype and they’re connected to HTML side using these lines:

this.username = this.authForm.controls['username'];	    
this.password = this.authForm.controls['password'];		

On the other hand, we will need to access our form directly:

<form [formGroup]="authForm" (ngSubmit)="onSubmit(authForm.value)">	

In auth.ts :

authForm: FormGroup;	

The form is initialized like this in auth.js:

this.authForm = formBuilder.group({
	username: ['', Validators.compose([Validators.required, Validators.pattern('[a-zA-Z]*'), Validators.minLength(8), Validators.maxLength(30)])],
	password: ['', Validators.compose([Validators.required, Validators.minLength(8)])]
});

We’re using FormBuilder helper service formBuilder to group form fields into ControlGroup object called authForm.

I hope all this make sense. It’s almost funny, but validation is the easiest part here. 🙂

Everything shown here makes an Angular 2 form, but what about validation?

Let’s once again take a look at these lines:

username: ['', Validators.compose([Validators.required, Validators.pattern('[a-zA-Z]*'), Validators.minLength(8), Validators.maxLength(30)])],
password: ['', Validators.compose([Validators.required, Validators.minLength(8)])]

We have defined four validation rules for the username field and two validation rules for the password filed. All these rules except pattern are predefined and you can use them any way you want. While the pattern is also preexisting validation rule you still need to provide a working regex pattern.

<ion-item [ngClass]="{'error-border':!authForm.controls.password.valid && authForm.controls.password.touched}">
	<ion-label floating>Password</ion-label>
	<ion-input formControlName="password" type="password"></ion-input>
</ion-item>
<ion-item *ngIf="authForm.controls.password.hasError('required') && authForm.controls.password.touched">
	<p>Sorry, field password is required!</p>
</ion-item>
<ion-item *ngIf="authForm.controls.password.hasError('minlength') && authForm.controls.password.touched">
	<p>Sorry, minimum password length is 8!</p>
</ion-item>

If the password field is not valid (!authForm.controls.password.valid) after a field has been touched (authForm.controls.password.touched) Angular will add a class error-border to ion-item element:

<ion-item [ngClass]="{'error-border':!authForm.controls.password.valid && authForm.controls.password.touched}">

authForm.controls.password keyword is a reference to password Control object.

At the same time, below each field, we can find a few additional div elements holding error descriptions. In the case of fields (username or password) have validation errors, a related description will become visible.

<ion-item *ngIf="authForm.controls.password.hasError('required') && authForm.controls.password.touched">
	<p>Sorry, field password is required!</p>
</ion-item>
<ion-item *ngIf="authForm.controls.password.hasError('minlength') && authForm.controls.password.touched">
	<p>Sorry, minimum password length is 8!</p>
</ion-item>

Finally, if everything is OK and application form is successfully validated, we’ll enable submit button:

<button ion-button full color="primary" [disabled]="!authForm.valid" style="margin-top: 20px;" type="submit">Authorize</button>

Code

In the case, GitHub repository below is not available and you can only use the code provided below, this is what project folder looks like (image was taken from the Sublime text editor):

Ionic 2 Form Validation Folder Structure

app.component.ts
import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

import { HomePage } from '../pages/home/home';
import { AuthPage } from '../pages/auth/auth';

@Component({
  templateUrl: 'app.html'
})

export class MyApp {
  rootPage:any;

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
    platform.ready().then(() => {
      statusBar.styleDefault();
      splashScreen.hide();
      this.checkPreviousAuthorization();
    });
  }

  checkPreviousAuthorization(): void { 
    if((window.localStorage.getItem('username') === "undefined" || window.localStorage.getItem('username') === null) && 
       (window.localStorage.getItem('password') === "undefined" || window.localStorage.getItem('password') === null)) {
      this.rootPage = AuthPage;
    } else {
      this.rootPage = HomePage;
    }
  }  
}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
 
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { AuthPage } from '../pages/auth/auth';
 
@NgModule({
  declarations: [
    MyApp,
    HomePage,
    AuthPage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage,
    AuthPage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}
auth.html
<ion-header>
  <ion-navbar>
    <ion-title>Authorization Page</ion-title>
  </ion-navbar>
</ion-header>
 
 
<ion-content padding>
    <form [formGroup]="authForm" (ngSubmit)="onSubmit(authForm.value)">
        <ion-item [ngClass]="{'error-border':!authForm.controls.username.valid && authForm.controls.username.touched}">
            <ion-label floating>Username</ion-label>
            <ion-input formControlName="username" type="text"></ion-input>
        </ion-item>
 
        <ion-item *ngIf="authForm.controls.username.hasError('required') && authForm.controls.username.touched">
            <p>Sorry, field username is required!</p>
        </ion-item>
        <ion-item *ngIf="authForm.controls.username.hasError('pattern') && authForm.controls.username.touched">
            <p>Sorry, only small and capital letters are allowed!</p>
        </ion-item>        
        <ion-item *ngIf="authForm.controls.username.hasError('minlength') && authForm.controls.username.touched">
            <p>Sorry, minimum username length is 8!</p>
        </ion-item>
        <ion-item *ngIf="authForm.controls.username.hasError('maxlength') && authForm.controls.username.touched">
            <p>Sorry, maximum username length is 30!</p>
        </ion-item>
 
        <ion-item [ngClass]="{'error-border':!authForm.controls.password.valid && authForm.controls.password.touched}">
            <ion-label floating>Password</ion-label>
            <ion-input formControlName="password" type="password"></ion-input>
        </ion-item>
        <ion-item *ngIf="authForm.controls.password.hasError('required') && authForm.controls.password.touched">
            <p>Sorry, field password is required!</p>
        </ion-item>
        <ion-item *ngIf="authForm.controls.password.hasError('minlength') && authForm.controls.password.touched">
            <p>Sorry, minimum password length is 8!</p>
        </ion-item>                
 
        <button ion-button full color="primary" [disabled]="!authForm.valid" style="margin-top: 20px;" type="submit">Authorize</button>        
    </form>
</ion-content>
auth.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, AbstractControl } from '@angular/forms';
import { IonicPage, NavController, NavParams } from 'ionic-angular';

import { HomePage } from '../home/home';

@IonicPage()
@Component({
  selector: 'page-auth',
  templateUrl: 'auth.html',
})

export class AuthPage {

	authForm: FormGroup;

	constructor(public nav: NavController, public navParams: NavParams, public formBuilder: FormBuilder) {

		this.nav = nav;

	    this.authForm = formBuilder.group({
	        username: ['', Validators.compose([Validators.required, Validators.pattern('[a-zA-Z]*'), Validators.minLength(8), Validators.maxLength(30)])],
	        password: ['', Validators.compose([Validators.required, Validators.minLength(8)])]
	    });
	}

	onSubmit(value: any): void { 
	    if(this.authForm.valid) {
			window.localStorage.setItem('username', value.username);
			window.localStorage.setItem('password', value.password);

			this.nav.push(HomePage);
	    }
	}   
}
home.html
https://www.gajotres.net/wp-admin/post.php?post=10371&action=edit#
<ion-header>
  <ion-navbar hideBackButton>
    <ion-title>Home Page</ion-title>
  </ion-navbar>
</ion-header>
 
<ion-content padding>
  <h2>Welcome {{username}}</h2>
  <button ion-button full color="primary" style="margin-top: 20px;" (click)="logout()">Logout</button>
</ion-content>
home.ts
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
 
import { AuthPage } from '../auth/auth';
 
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
 
    username: string;
 
    constructor(public nav: NavController) {
        this.nav = nav;
        this.username = window.localStorage.getItem('username');
    }

	logout() {
        window.localStorage.removeItem('username');
        window.localStorage.removeItem('password');

        this.nav.setRoot(AuthPage);
        this.nav.popToRoot();         
	}    
}

Deployment

Next step, build our application:

ionic build android

Be careful here, this step may break if you’re behind a firewall. The first execution will take a long time, so be patient.

Finally:

ionic run android -l -c -s

or, as this example is not bound to any platform, you can run it in your browser:

ionic serve -l -c -s

Download The Code

Working GitHub repo link can be found below:

GitHub
Categories

Leave a Reply