Form Validation In An Angular Web Application With Custom Directives

When creating a web application that accepts user input, it is often a good idea to validate anything the user provides. While you should always validate this information via some backend server-side code, it often provides a good user experience to validate via the frontend as well. By doing form validation on the frontend, you set yourself up for the possibility to catch and display errors before form submission.

If you’ve been keeping up, I once demonstrated how to do form validation in AngularJS via an Ionic Framework application. As of now, AngularJS is ancient technology, so we’re going to see how to do the same with Angular. However, we’ll be seeing from the perspective of a web application, rather than a cross-platform hybrid mobile application.

We’re going to be basing everything in this guide off a new project created via the Angular CLI. This means that you should have installed the Angular CLI before going forward.

Creating a New Angular Project via the CLI

From the CLI, execute the following to create a new Angular project:

ng new validation-project

While we won’t be creating a custom validation directive from the start, let’s create this directive anyways so it is ready to go. Navigate into the freshly created project and execute the following via the CLI:

ng g directive email-validator

With the project created and the directive available, we are ready to start developing a form with client-side validation.

Using the Available Angular Form Validators

As part of Angular, you have access to a set of already available form validators. They are basic, but still very useful in any project that accepts user input.

Within the project’s src/app/app.component.html file, include the following HTML markup:

<form #form="ngForm" novalidate>
    <div>
        <label for="email">Email:</label>
        <input #email="ngModel" type="email" name="email" [(ngModel)]="input.email" required />
        <div *ngIf="email.errors && (email.dirty || email.touched)">
            <p *ngIf="email.errors.required">* Email is required</p>
        </div>
    </div>
    <div>
        <label for="password">Password:</label>
        <input #password="ngModel" type="password" name="password" [(ngModel)]="input.password" required minlength="5" />
        <div *ngIf="password.errors && (password.dirty || password.touched)">
            <p *ngIf="password.errors.required">* Password is required</p>
            <p *ngIf="password.errors.minlength">* The minimum length was not met</p>
        </div>
    </div>
    <button [disabled]="form.invalid">Login</button>
</form>

In the above markup we have a form with two input fields as well as a button that, in theory, would submit the form. The form uses a template variable set to ngForm and the input fields use template variables set to ngModel. We’re doing this so we can view various validation information about each.

Notice the first form element that represents email input. We are saying that it is a required field with two-way binding on a variable that we’ll later set within the TypeScript code.

Next notice some DOM code bound to an Angular *ngIf structural directive. If the input element has any errors and it has actually been altered or blurred, then print them on the screen.

Available validation attributes are as follows:

  • valid - A boolean based on whether your rules are valid or not
  • invalid - A boolean based on whether your rules are invalid or not
  • pristine - True if the form or input value has not been used yet
  • dirty - True if the form or input has been used
  • touched - True if the input has been blurred

These are the same as what was available in AngularJS.

The form itself will track the validity of each of the inner form elements. This means that if any are invalid, we’ll be disabling the button. It will change if the form becomes valid.

In a perfect world, Angular will be able to validate input types. For example, if the input field is an email field, it should be invalid if not an email. For whatever reason, this wasn’t working for me, so I came up with a custom form validation technique.

Creating a Custom Angular Form Validator

This is where our directive that we had created comes into play. The goal here is to use a regular expression to validate any data entered into the field. The regular expression will throw a form validation error if the data is not an email.

Open the project’s src/app/email-validator.directive.ts file and include the following TypeScript code:

import { Directive } from '@angular/core';
import { NG_VALIDATORS, Validator, AbstractControl } from '@angular/forms';

@Directive({
    selector: '[isEmail]',
    providers: [{provide: NG_VALIDATORS, useExisting: EmailValidatorDirective, multi: true}]
})
export class EmailValidatorDirective implements Validator {

    public constructor() {}

    public validate(control: AbstractControl): {[key: string]: any} {
        let emailRegEx = /^(([^<>()\[\]\\.,;:\[email protected]"]+(\.[^<>()\[\]\\.,;:\[email protected]"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
        let valid = emailRegEx.test(control.value);
        return control.value < 1 || valid ? null : {'isEmail': true};
    }

}

In the above code we’re creating an attribute directive called isEmail that can be used from any HTML input tag. Within the providers array of the @Directive block, we add NG_VALIDATORS and multi to indicate it is a form validator, and we use the existing EmailValidatorsDirective that follows.

You’ll notice that there is a long and nasty regular expression in the validate method. I cannot claim credit for this expression. Instead, this is an email validation regular expression that I took from Email RegEx.

The validate method says that if our form element is empty, or we have a valid email, then return a null value. A null value indicates that our element is valid. If it is not valid, return something that we can use via the HTML.

Now let’s edit our HTML markup to take in this new validation attempt:

<form #form="ngForm" novalidate>
    <div>
        <label for="email">Email:</label>
        <input #email="ngModel" type="email" name="email" [(ngModel)]="input.email" required isEmail />
        <div *ngIf="email.errors && (email.dirty || email.touched)">
            <p *ngIf="email.errors.required">* Email is required</p>
            <p *ngIf="email.errors.isEmail">* Email must be valid</p>
        </div>
    </div>
    <div>
        <label for="password">Password:</label>
        <input #password="ngModel" type="password" name="password" [(ngModel)]="input.password" required minlength="5" />
        <div *ngIf="password.errors && (password.dirty || password.touched)">
            <p *ngIf="password.errors.required">* Password is required</p>
            <p *ngIf="password.errors.minlength">* The minimum length was not met</p>
        </div>
    </div>
    <button [disabled]="form.invalid">Login</button>
</form>

In the above HTML, we’ve added isEmail to the input field and we’ve also added another possible error message. If the email is invalid, we would have returned true in our custom directive.

Conclusion

You just saw how to use form validation in your Angular web application. We used the already available validators as well as created our own via a custom attribute directive with regular expressions. While we used a regular expression to determine validity, you can do whatever you want in this custom validator.

For more reading on validation, check out the official Angular documentation. It took me some time to make sense of it which is why I thought I’d share my explanation.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.

Search

Follow Us

Subscribe

Subscribe to my newsletter for monthly tips and tricks on subjects such as mobile, web, and game development.

Subscribe on YouTube

Support This Site