In this tutorial you will learn everything about Ionic forms and input validations in Ionic apps.

We will discuss the best practices for designing user-friendly forms with Ionic. Then we will go through the differences between Angular template driven and reactive forms.

For this Ionic tutorial we created a mobile app example with lots of forms and validations to help you master data collection using Angular reactive forms and also some techniques to excel the user experience.

You can check the online demo of the Ionic 5 example app we will build for this form tutorial.

I will explain to you the core concepts of: FormControl, FormGroup, FormBuilder, and Validators and show you examples, so you can learn to use them properly.

We want this to be the best Ionic 5 forms tutorial, so we created advanced custom validators to show you how to validate passwords, phone numbers and unique usernames.

Working With Forms

Forms are the pillar of any business applications. You can use forms to perform countless data-entry tasks such as: login, submit a request, place an order, book a flight or create an account.

When developing a form, it's important to create a good data-entry experience to efficiently guide the user through the workflow.

Developing good forms requires design and user experience skills, as well as a framework with support for two-way data binding, change tracking, validation, and error handling such as Angular. As you may know, Ionic is built on top of Angular. (if you didn't know this, you should consider reading Ionic Framework introduction and key components).

Forms are usually one of the major interaction points between an app and the user, allowing them to send data to the application. Commonly, the data is sent to the web server, but the app can also intercept it to use it on its own.

Let's keep in mind that the real purpose of forms is data collection. There are different ways of sending and handling that data in the server, and depends a lot on your backend tech stack and the nature of your business logic, so we are not going to cover this. However, if you are interested in building a complete mobile app with Ionic and the MEAN (MongoDB, Express, Angular and NodeJS) stack, you should check Build a complete mobile app with Ionic.

In this tutorial you will learn everything about Ionic forms and input validations in Ionic apps.

In the following section you will learn how to build an Ionic example app with forms and validations. We will add both basic, advanced and custom form controls. Then we will show you how to add custom error messages for your validations. Remember you can download all the source code using the GET THE CODE button from above.

If you are looking for a super polished user interface, and you want to save time by getting a ready-made Ionic 5 starter app which features lots of forms and validation examples you should definitely check Ionic 6 Full Starter App.

These are some forms you can find in Ionic 6 Full Starter App. There are also more complex forms such as beautiful filters, ratings, validations, and many more!

Ionic forms example
Ionic forms example
Ionic forms example

Angular Reactive Forms vs Template Driven Forms

Angular offers two form-building technologies: reactive forms and template-driven forms. These two belong to the @angular/forms library and share a series of form control classes. However, they notably diverge in terms of philosophy, programming style and technique. They even have their own modules: ReactiveFormsModule and FormsModule.

Another important difference is that Reactive forms are synchronous while. Template-driven forms are asynchronous.

Before we continue with this Ionic example app, let's clarify what's the difference between Reactive Forms and Template-driven Forms from a high level perspective.

Angular Reactive Forms

Angular reactive forms help the reactive programming style that favors an explicit data management flow between non-UI data models (typically retrieved from a server) and a UI-oriented form model that keeps the states and values of HTML controls on the app screen. Reactive forms offer an easy way to use reactive patterns, testings and validations.

When using reactive forms (also known as model-driven forms), we will avoid directives like ngModel, NgForm, required and such. The idea is that instead of declaring that, we actually use the underlying APIs to do it for us and so make Angular power things for us. In a sense, instead of binding Object models to directives like in template-driven forms, we create our own instances inside a component class and construct our very own JavaScript models.

This approach has a lot more power and is extremely productive to work with since it allows us to write expressive code that is very testable and keeps all logic in the same place, instead of dividing it around different form templates.

With reactive forms, you are able to create and manipulate form control objects directly in the Component. Since the component class has access to the form control structure and to the data model, you can push data model values into the form controls and pull values that has been changed by the user back out. The component is able to observe changes in the form control state and react to these changes, for example to show a validation message.

One of the advantages of working directly with form control objects is that value and validity updates are always synchronous and under your control. You won't find the timing issues that sometimes affect a template-driven form. Also, reactive forms can be easier to unit test.

Angular Template driven forms

On the other hand, Template-driven forms take a completely different approach.

You can place HTML form controls (such as <input> or <select>) in the component template and bind them to data model properties in the component, using directives like ngModel.

In template-driven forms, you don't create Angular form control objects. Angular directives create them for you using information from your data bindings. You don't have to push and pull data values because Angular handles that for you through the ngModel directive. Angular updates the mutable data model according to user changes as they happen.

Examples of these directives are the ngModel, required, minlength and so. On an advanced level, this is what template-driven forms achieve for us — by specifying directives to bind our models, values, validations and more, we are letting the template do all the work on the background.

While this approach means less code in the component class, the template-driven forms are asynchronous which may complicate the development in more advanced scenarios.

Template driven forms may resemble more the way forms used to be in AngularJS (v 1.0), maybe that's why people still use them as a familiar option.

Which one is the best? Reactive or template-driven? Neither is the best. They're two different architectural paradigms with their own pros and cons. You can choose the approach that works best for you. This way you are free to decide to use both in the same application.

In this Ionic forms tutorial we will use Reactive Forms.

Angular forms basics: FormGroup, FormControl and FormBuilder

As we mentioned before, we are going to use Angular reactive forms in this example app. These are the basic concepts you need to grasp before we start.

FormControl: it tracks the value and validity status of an Angular form control. It matches to an HTML form control such as an input or a selector.

This basic example shows a FormControl for the name property which should not be empty.

ts file:

this.name = new FormControl('Dayana', Validators.required)

html file:

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

FormGroup: it tracks the value and validity state of a FormBuilder instance group. It aggregates the values of each child FormControl into one object, with each form control name as the key. Furthermore, it calculates its status by reducing the statuses of its children. For example, if one of the controls in a group is invalid, the entire group becomes invalid. For example:

this.user = new FormGroup({
 name: new FormControl('Dayana', Validators.required),
 country: new FormControl('Uruguay', Validators.required)
});

FormBuilder: is a helper class that creates FormGroup, FormControl and FormArray instances for us. It basically reduces the repetition and clutter by handling details of form control creation for you.

this.validations_form = this.formBuilder.group({
  name: new FormControl('', Validators.required),
  email: new FormControl('', Validators.compose([
    Validators.required,
    Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
  ]))
});

All of them should be imported from the @angular/forms module.

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

Great UX => Improve conversions

Forms can be the bottleneck of the conversion funnel, as they require the user to perform a task much more complicated than just a click, by thinking and typing all the required information by the form. This may be much more frustrating in mobile devices and narrow screens, which is why you have to strive to do your best to have an awesome user experience to reduce users frustrations and ultimately improve your funnel conversion.

Some people say that user experience is often more important than what the app offers itself. So before you start coding, it's important to take the time and analyze which fields are needed. When designing the mock-up, try to include just the fields that are imperative to you, knowing that the bigger the form, the bigger chance you have of losing a user.

Keep simplicity in mind by asking only what you absolutely need.

Did you know that we recently released Ionic 6 Full Starter App? It's an Ionic 5 template that you can use to jump start your Ionic app development and save yourself hundreds of hours of design and development.

It is also a PWA and has beautiful examples of forms and validations.Try it on your phone to see how it works!

Ionic 5 starter

It is not part of the scope of the article to go beyond the basics of forms UX, but if you want to learn more about how to design better forms, we recommend checking these articles:

From a design perspective, Ionic has a wide collection of nice form elements and form inputs for you to choose from. On the other hand, from a UX point of view, we are going to use angular form validations to improve the experience of our Ionic mobile app.

A quick improvement that can be easily implemented in your Ionic app is form validation. Best practices say that you should always validate all data entered by the user through the backend, because of security issues. However, by validating through the frontend, you can improve the user experience and perceived response time.

Angular has its own validators that work great in Ionic apps. Let's see how to use some of the Angular form validators to make our application significantly better for users.

Validation timing — When should we perform the form validation?

The key is to avoid interrupting and disturbing the user. Also, it is not recommended to do all the validations at the end, when the user submits the form. Imagine that scenario, the user spent some time completing each entry (without knowing the limitations beforehand) and finally when he thinks the task is finished, the app shows him multiple error messages caused by invalid entries. Frustrating!

A better alternative may be to consider these options:

  • Real time: It means validating as you type. These are super handy and are the default for Angular Validators.

  • On blur: It can be less disturbing for certain cases as we only validate when the user is focused outside of the form input. However, the user will only see the errors once they move on to the next entry.

Designing our Form UX and Input Validation requirements

The goal of this post is to show you how to achieve Ionic forms validations, so we will go through as many form validation examples as possible. It is important to take some time to write your form and validation requirements before starting, in our case, this is the data we want to collect with its corresponding restrictions.

  • Username
    • Min length (5)
    • Max length (25)
    • Must contain numbers and letters
    • The username must be unique
    • Required
  • Name
    • Required
  • Last name
    • Required
  • Email
    • Valid email
    • Required
  • Country selection
    • Valid country from a dropdown
  • Phone
    • The phone must be valid for the selected country
    • Should accept only numbers
    • Required
  • Gender
    • Pick one of the provided list
  • Password
    • Min length (5)
    • Must contain letters (both uppercase and lowercase) and numbers
    • Must re-type password to ensure correctness
    • Required
  • Terms Checkbox
    • Must accept terms

Let's put what we've learned so far into practice and start building our Ionic 5 form handling and validation sample application. Here's what we're going to build in this section:

Forms and Validation in Ionic
Forms and Validation in Ionic

In reactive forms, instead of adding validators via attributes in the template (as we do in template-driven forms), we add validation functions directly to the form control model in the Angular component class. Angular then calls these functions every time the value of the control changes.

You can choose to write your own validation functions, or you can use some of Angular's built-in validators.

Built-in validators are stock validators provided by Angular. For a full list of Angular built-in validators, see the Validators API reference. However, built-in validators won't always match the exact use case of your application, so sometimes you will want to create a custom validator for your Ionic app.

In this Ionic Forms tutorial, we will explain how to create and use both types of validators.

In this forms tutorial we will use the following Angular built-in validators to validate our form inputs:

required: Validator that requires controls to have a non-empty value. It also validates that the value matches the input type. For example, if the input is of “email” type, then the input will be valid if it’s not empty and if the value is of email type.

minLength: Validator that requires controls to have a value of a minimum length.

maxLength: Validator that requires controls to have a value of a maximum length.

pattern: Validator that requires a control to match a regex to its value. You can find more information about regex patterns in the PatternValidator reference.

email: Validator that performs email validation.

compose: is used when more than one validation is needed for the same form field.

Let’s start with some easy validations for the name, last name and email inputs. As we defined before, we have the following requirements for our form controls:

  • Name: required
  • Last name: required
  • Email: valid email and required
  • Terms: must accept terms and conditions (checkbox checked validation)

So we will create a FormControl for each of them. Check the following typescript code to see how you can achieve this.

name: new FormControl('', Validators.required),
lastname: new FormControl('', Validators.required),
email: new FormControl('', Validators.compose([
   Validators.required,
   Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
 ])),
terms: new FormControl(true, Validators.pattern('true'))

We can get very creative and build any kind of custom validators. In this post we will be implementing three in particular. One to validate that the username is unique (checks against the server if the username is already taken), another one that implements the confirmation password validation to ensure the user typed the desired password correctly, and a third one that validates that the phone is valid for the selected country.

Ionic username custom validator

The goal of this custom Angular validator is to validate that the username is available. We added the following code in a new username.validator.ts file:

import { FormControl } from '@angular/forms';
export class UsernameValidator {
  static validUsername(fc: FormControl){
    if(fc.value.toLowerCase() === "abc123" || fc.value.toLowerCase() === "123abc"){
      return ({validUsername: true});
    } else {
      return (null);
    }
  }
}

With fc.value we obtain the value in the username field, so we can control the usernames we won't allow entering.

Then, in the typescript file where we have defined our form, we should import our custom validator.

import { UsernameValidator } from '../../validators/username.validator';

this.validations_form = this.formBuilder.group({
  username: new FormControl('', Validators.compose([
    UsernameValidator.validUsername,
    Validators.maxLength(25),
    Validators.minLength(5),
    Validators.pattern('^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$'),
    Validators.required
  ])),
})

We used minLength(5) and maxLength(25) to ensure the minimum and maximum length of the value. We also used required to avoid this input to be left empty, and ng-pattern="^(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]+$" to force a correct input value containing letters and numbers.

Notice how we used the UsernameValidator.validUsername custom directive to validate that the username is not already taken. This is the implementation of the custom directive, as we don’t have a dedicated backend and database for this Ionic app example, we hard coded two existing usernames (“abc123” and “123abc”).

Ionic phone and country advanced validator

The purpose of this custom Angular validator is to validate that a phone number belongs to a certain country.

We will use Google Libphonenumber JavaScript library for parsing, formatting, and validating international phone numbers. To install it you should run:

npm install --save-prod google-libphonenumber

We added the following code in a new phone.validator.ts file to create our PhoneValidator:

import { AbstractControl, ValidatorFn } from '@angular/forms';
import libphonenumber from 'google-libphonenumber';

export class PhoneValidator {

   // Inspired on: https://github.com/yuyang041060120/ng2-validation/blob/master/src/equal-to/validator.ts
  static validCountryPhone = (countryControl: AbstractControl): ValidatorFn => {
    let subscribe: boolean = false;

    return (phoneControl: AbstractControl): {[key: string]: boolean} => {
      if (!subscribe) {
        subscribe = true;
        countryControl.valueChanges.subscribe(() => {
          phoneControl.updateValueAndValidity();
        });
      }
      if(phoneControl.value !== ""){
        try{
          const phoneUtil = libphonenumber.PhoneNumberUtil.getInstance();
          let phoneNumber = "" + phoneControl.value + "",
              region = countryControl.value.iso,
              number = phoneUtil.parse(phoneNumber, region),
              isValidNumber = phoneUtil.isValidNumber(number);
          if(isValidNumber){
            return null;
          }
        }catch(e){
          return {
            validCountryPhone: true
          };
        }
        return {
          validCountryPhone: true
        };
      }
      else{
        return null;
      }
    };
  };
}

The phone directive controls that the value from the phone number input is correct for the selected country.

Then, in the typescript file where we have defined our form (form.page.ts), we should import our custom validator. In form.page.ts we have a FormGroup with the phone input and the country selector so when the value changes in one of these fields the directive checks if both are correct.

this.country_phone_group = new FormGroup({
  country: new FormControl(this.countries[0], Validators.required),
  phone: new FormControl('', Validators.compose([
    Validators.required,
    PhoneValidator.validCountryPhone(country)
  ]));
});

Ionic password validation

Let’s continue with the password form control validations. As we mention before, we have the following requirements for our password input form:

  • Required
  • Min length (5)
  • Must contain letters (both uppercase and lowercase) and numbers
  • Must re-type password to ensure correctness

We added the following code in a new password.validator.ts file to create our PasswordValidator which validates that the password was re-typed correctly:

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

export class PasswordValidator {
// Inspired on: http://plnkr.co/edit/Zcbg2T3tOxYmhxs7vaAm?p=preview
static areEqual(formGroup: FormGroup) {
  let val;
  let valid = true;

  for (let key in formGroup.controls) {
    if (formGroup.controls.hasOwnProperty(key)) {
      let control: FormControl = <FormControl>formGroup.controls[key];
      if (val === undefined) {
        val = control.value
      } else {
        if (val !== control.value) {
          valid = false;
          break;
        }
      }
    }
  }
  if (valid) {
    return null;
  }
  return {
    areEqual: true
  }
 }
}

Then, in the typescript file where we have defined our form (form.page.ts), we should import our custom password validator and add the other simple validations: password no shorter than 5 chars, and with letters and numbers.

import { PasswordValidator } from '../../validators/password.validator';
this.matching_passwords_group = new FormGroup({
  password: new FormControl('', Validators.compose([
     Validators.minLength(5),
     Validators.required,
     Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$') //this is for the letters (both uppercase and lowercase) and numbers validation
  ])),
  confirm_password: new FormControl('', Validators.required)
}, (formGroup: FormGroup) => {
   return PasswordValidator.areEqual(formGroup);
});

From a UX perspective, it is also very important to display the proper error messages to guide the user in the right direction.

We will define a list of possible error messages in the typescript file where we have defined our form (form.page.ts).

validation_messages = {
'username': [
  { type: 'required', message: 'Username is required.' },
  { type: 'minlength', message: 'Username must be at least 5 characters long.' },
  { type: 'maxlength', message: 'Username cannot be more than 25 characters long.' },
  { type: 'pattern', message: 'Your username must contain only numbers and letters.' },
  { type: 'validUsername', message: 'Your username has already been taken.' }
],
'name': [
  { type: 'required', message: 'Name is required.' }
],

.... //more messages
}

Then, in our HTML file we must iterate the list and show the right message.

<ion-item>
<ion-label position="floating" color="primary">Username</ion-label>
<ion-input type="text" formControlName="username" class="form-controll" required></ion-input>
</ion-item>
<div class="validation-errors">
<ng-container *ngFor="let validation of validation_messages.username">
  <div class="error-message" *ngIf="validations_form.get('username').hasError(validation.type) && (validations_form.get('username').dirty || validations_form.get('username').touched)">
    {{ validation.message }}
  </div>
</ng-container>
</div>

It's a common error to think that accessibility is an optional feature in mobile app development. Both iOS and Android provide accessibility tools to help developers create an accessible UX for all users, including those with a disability or impairment. Despite the tools being provided by the platform, the overall accessibility of any app depends in large part on the developer's markup, additional styling, and scripting they've included.

Keep in mind that not only disabled people benefit from good accessibility. Nowadays, voice assistants like Siri and Alexa are becoming ubiquitous, and haptic feedback (e.g. vibrations) are a part of Google's Material Design guidelines. For example, some users might want to navigate an app while driving a car or cooking dinner, without having to touch the device.

As mentined earlier, forms can be very important for our application and are constantly used to collect information from users, so it's very important to make forms accessible to ALL users. This means that when building our forms, we must code and design with accessibility practices in mind.

This guide mentions some good practices about Forms, Form Validation, and CAPTCHAs. Also, make sure to check this guide on form concepts to create websites that meet WCAG.

Well, in this Ionic Forms tutorial we show you how to handle inputs and validations in Ionic. Now that you have learned more about collecting data using forms and some techniques to improve the user experience, you can focus on:

Did you know that we also offer premium templates that can save you thousands of hours of design and development?