HI WELCOME TO SIRIS

Ionic 4 – Form Validation with Reactive Forms Tutorial

Leave a Comment

 Angular Reactive Forms will let your application listen to the changes of input values within the forms as an Observable and react to those changes accordingly. This is useful for making a real-time form validation for Ionic application. In this tutorial, we will be making a form that validates the user input in real-time.

  1. Create a New Ionic Project
  2. Set Up Reactive Form
  3. Create the Form
  4. Validate the Form
  5. Result
  6. Complete Source Code
  7. Video

This tutorial was made with Angular + Ionic CLI v4 and above.

1. Create a New Ionic Project

Start this project by creating new blank Ionic project.

ionic start ionic-reactive-forms blank --type=angular

2. Set Up Reactive Form

Since we are using the reactive form, go to home.module.ts and replace the FormsModule with ReactiveFormsModule.

Add the following changes inside home.module.ts.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
// import { FormsModule } from '@angular/forms';
import { ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { HomePage } from './home.page';
@NgModule({
imports: [
CommonModule,
// FormsModule,
ReactiveFormsModule,
IonicModule,
RouterModule.forChild([
{
path: '',
component: HomePage
}
])
],
declarations: [HomePage]
})
export class HomePageModule {}

After that, go to home.page.ts and import FormBuilder from angular/forms.

Add the following changes inside home.page.ts.

import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss']
})
export class HomePage {
constructor(private formBuilder: FormBuilder) {}
}

3. Create the Form

Creating form control instances manually is tedious especially for a complex form. With FormBuilder, it reduces the amount of boilerplate when building a form. Read more about FormBuilder.

The following is an example of the how using the FormBuilder reduces the amount of boilerplate used for the same task.

// Without FormBuilder
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl('')
});
// With FormBuilder
profileForm = this.formBuilder.group({
firstName: '',
lastName: ''
});

For this tutorial, the form consists of a nameemailphone number, and address. We are also going to group together several inputs to make up a complete address input.

Add the following FormBuilder group inside home.page.ts

registrationForm = this.formBuilder.group({
name: [''],
email: [''],
phone: [''],
address: this.formBuilder.group({
street: [''],
city: [''],
state: [''],
zip: ['']
})
});

For submission, create a function named submit() for form submission. The function will print the value of the submission to the console screen.

Add the following function inside home.page.ts.

public submit() {
console.log(this.registrationForm.value);
}

Create the form inside home.page.html using form tag and specify the form identifier with [formGroup]="registrationForm".

Map the ion-input with the FormControl using formControlName. As for the FormGroup, use formGroupName.

For submission, use event-emitter ngSubmit to execute the submit() function that we created earlier.

Add the following changes inside home.page.html.

<form [formGroup]="registrationForm" (ngSubmit)="submit()">
<ion-list>
<ion-item>
<ion-label position="floating">Name</ion-label>
<ion-input autocapitalize inputmode="text" formControlName="name"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Phone</ion-label>
<ion-input inputmode="tel" formControlName="phone"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Email</ion-label>
<ion-input inputmode="email" formControlName="email"></ion-input>
</ion-item>
</ion-list>
<ion-list formGroupName="address">
<ion-list-header>
<ion-label>Address</ion-label>
</ion-list-header>
<ion-item>
<ion-label position="floating">Street</ion-label>
<ion-input formControlName="street"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">City</ion-label>
<ion-input formControlName="city"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">State</ion-label>
<ion-input formControlName="state"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Zip Code</ion-label>
<ion-input formControlName="zip"></ion-input>
</ion-item>
</ion-list>
<ion-button [disabled]="!registrationForm.valid" type="submit" expand="block">Submit <ion-icon slot="end"
name="create">
</ion-icon>
</ion-button>
</form>

4. Validate the Form

For form validation, go to home.page.ts and import Validators from angular/forms.

Add the following changes inside home.page.ts.

import { Component } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";

Create a getter for every input we can call it easily inside home.page.html.

Add the following getters inside home.page.ts.

get name() {
return this.registrationForm.get("name");
}
get email() {
return this.registrationForm.get("email");
}
get phone() {
return this.registrationForm.get('phone');
}
get street() {
return this.registrationForm.get('address.street');
}
get city() {
return this.registrationForm.get('address.city');
}
get state() {
return this.registrationForm.get('address.state');
}
get zip() {
return this.registrationForm.get('address.zip');
}

For an error message, we are going to specify it inside the home.page.ts.

Add the following inside home.page.ts.

public errorMessages = {
name: [
{ type: 'required', message: 'Name is required' },
{ type: 'maxlength', message: 'Name cant be longer than 100 characters' }
],
email: [
{ type: 'required', message: 'Email is required' },
{ type: 'pattern', message: 'Please enter a valid email address' }
],
phone: [
{ type: 'required', message: 'Phone number is required' },
{ type: 'pattern', message: 'Please enter a valid phone number' }
],
street: [
{ type: 'required', message: 'Street name is required' },
{
type: 'maxlength',
message: 'Street name cant be longer than 100 characters'
}
],
city: [
{ type: 'required', message: 'City name is required' },
{
type: 'maxlength',
message: 'City name cant be longer than 100 characters'
}
],
state: [
{ type: 'required', message: 'State is required' },
{
type: 'maxlength',
message: 'State cant be longer than 100 characters'
}
],
zip: [
{ type: 'required', message: 'Zip code is required' },
{
type: 'pattern',
message: 'Please enter a valid zip code'
}
]
};

Insert an error messages below every input.

Add the following changes inside home.page.html.

<form [formGroup]="registrationForm" (ngSubmit)="submit()">
<ion-list>
<ion-item>
<ion-label position="floating">Name</ion-label>
<ion-input autocapitalize inputmode="text" formControlName="name"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.name">
<ng-container *ngIf="name.hasError(error.type) && (name.dirty || name.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">Phone</ion-label>
<ion-input inputmode="tel" formControlName="phone"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.phone">
<ng-container *ngIf="phone.hasError(error.type) && (phone.dirty || phone.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">Email</ion-label>
<ion-input inputmode="email" formControlName="email"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.email">
<ng-container *ngIf="email.hasError(error.type) && (email.dirty || email.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
</ion-list>
<ion-list formGroupName="address">
<ion-list-header>
<ion-label>Address</ion-label>
</ion-list-header>
<ion-item>
<ion-label position="floating">Street</ion-label>
<ion-input formControlName="street"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.street">
<ng-container *ngIf="street.hasError(error.type) && (street.dirty || street.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">City</ion-label>
<ion-input formControlName="city"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.city">
<ng-container *ngIf="city.hasError(error.type) && (city.dirty || city.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">State</ion-label>
<ion-input formControlName="state"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.state">
<ng-container *ngIf="state.hasError(error.type) && (state.dirty || state.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">Zip Code</ion-label>
<ion-input formControlName="zip"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.zip">
<ng-container *ngIf="zip.hasError(error.type) && (zip.dirty || zip.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
</ion-list>
<ion-button [disabled]="!registrationForm.valid" type="submit" expand="block">Submit <ion-icon slot="end"
name="create">
</ion-icon>
</ion-button>
</form>

Change the color of the error message inside home.page.scss.

Add the following changes inside home.page.scss.

.error-message {
color: var(--ion-color-danger);
}

5. Result

Result


6. Complete Source Code

Feel free use the complete source code inside your awesome project.

home.page.html
<ion-header>
<ion-toolbar>
<ion-title>
User Details
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<div class="ion-padding">
<form [formGroup]="registrationForm" (ngSubmit)="submit()">
<ion-list>
<ion-item>
<ion-label position="floating">Name</ion-label>
<ion-input autocapitalize inputmode="text" formControlName="name"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.name">
<ng-container *ngIf="name.hasError(error.type) && (name.dirty || name.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">Phone</ion-label>
<ion-input inputmode="tel" formControlName="phone"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.phone">
<ng-container *ngIf="phone.hasError(error.type) && (phone.dirty || phone.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">Email</ion-label>
<ion-input inputmode="email" formControlName="email"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.email">
<ng-container *ngIf="email.hasError(error.type) && (email.dirty || email.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
</ion-list>
<ion-list formGroupName="address">
<ion-list-header>
<ion-label>Address</ion-label>
</ion-list-header>
<ion-item>
<ion-label position="floating">Street</ion-label>
<ion-input formControlName="street"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.street">
<ng-container *ngIf="street.hasError(error.type) && (street.dirty || street.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">City</ion-label>
<ion-input formControlName="city"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.city">
<ng-container *ngIf="city.hasError(error.type) && (city.dirty || city.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">State</ion-label>
<ion-input formControlName="state"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.state">
<ng-container *ngIf="state.hasError(error.type) && (state.dirty || state.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
<ion-item>
<ion-label position="floating">Zip Code</ion-label>
<ion-input formControlName="zip"></ion-input>
</ion-item>
<div *ngFor="let error of errorMessages.zip">
<ng-container *ngIf="zip.hasError(error.type) && (zip.dirty || zip.touched)">
<small class="error-message">{{error.message}}</small>
</ng-container>
</div>
</ion-list>
<ion-button [disabled]="!registrationForm.valid" type="submit" expand="block">Submit <ion-icon slot="end"
name="create">
</ion-icon>
</ion-button>
</form>
</div>
</ion-content>

home.page.scss
.error-message {
color: var(--ion-color-danger);
}
home.page.ts
import { Component } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
@Component({
selector: "app-home",
templateUrl: "home.page.html",
styleUrls: ["home.page.scss"]
})
export class HomePage {
constructor(private formBuilder: FormBuilder) {}
get name() {
return this.registrationForm.get("name");
}
get email() {
return this.registrationForm.get('email');
}
get phone() {
return this.registrationForm.get('phone');
}
get street() {
return this.registrationForm.get('address.street');
}
get city() {
return this.registrationForm.get('address.city');
}
get state() {
return this.registrationForm.get('address.state');
}
get zip() {
return this.registrationForm.get('address.zip');
}
public errorMessages = {
name: [
{ type: 'required', message: 'Name is required' },
{ type: 'maxlength', message: 'Name cant be longer than 100 characters' }
],
email: [
{ type: 'required', message: 'Email is required' },
{ type: 'pattern', message: 'Please enter a valid email address' }
],
phone: [
{ type: 'required', message: 'Phone number is required' },
{ type: 'pattern', message: 'Please enter a valid phone number' }
],
street: [
{ type: 'required', message: 'Street name is required' },
{
type: 'maxlength',
message: 'Street name cant be longer than 100 characters'
}
],
city: [
{ type: 'required', message: 'City name is required' },
{
type: 'maxlength',
message: 'City name cant be longer than 100 characters'
}
],
state: [
{ type: 'required', message: 'State is required' },
{
type: 'maxlength',
message: 'State cant be longer than 100 characters'
}
],
zip: [
{ type: 'required', message: 'Zip code is required' },
{
type: 'pattern',
message: 'Please enter a valid zip code'
}
]
};
registrationForm = this.formBuilder.group({
name: ['', [Validators.required, Validators.maxLength(100)]],
email: [
'',
[
Validators.required,
Validators.pattern('^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}$')
]
],
phone: [
'',
[
Validators.required,
Validators.pattern('^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-s./0-9]*$')
]
],
address: this.formBuilder.group({
street: ['', [Validators.required, Validators.maxLength(100)]],
city: ['', [Validators.required, Validators.maxLength(100)]],
state: ['', [Validators.required, Validators.maxLength(100)]],
zip: [
'',
[Validators.required, Validators.pattern('^[0-9]{5}(?:-[0-9]{4})?$')]
]
})
});
public submit() {
console.log(this.registrationForm.value);
}
}
home.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
// import { FormsModule } from '@angular/forms';
import { ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { HomePage } from './home.page';
@NgModule({
imports: [
CommonModule,
// FormsModule,
ReactiveFormsModule,
IonicModule,
RouterModule.forChild([
{
path: '',
component: HomePage
}
])
],
declarations: [HomePage]
})
export class HomePageModule {}

0 comments:

Post a Comment

Note: only a member of this blog may post a comment.