Custom Form Validators in Angular 6 Using Regular Expressions

“Black and white shot of urban building windows with shadow, Grande Arche de la Defense” by Benjamin Bousquet on Unsplash

While Angular does provide various input field validators (i.e. email), there are use cases where you will need to create your own validators. That’s when the power of regular expressions shines brightly.

What are Regular Expressions?

So what are regular expressions (abbr. regexp)?

Regular expressions are patterns used to match character combinations in strings.

Regular expressions can be defined in two simple ways:

let re = /ab+c/;

Or calling the constructor function of the RegExp object:

let re = new RegExp(‘ab+c’);

However, I won’t go into too many details in regards to regular expressions because the sole focus of this article is Angular. There are many online resources on how to use regular expressions properly, so here is one which comes in handy.

Reactive Form Validation

Considering that Angular has two distinct approaches when it comes to implementation of the forms, our focus will be on reactive forms, the other one being template driven approach.

Now let’s get back to the basics on how to initiate a reactive form. First, you will need to import the following line into your app’s module file:

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

Also, don’t forget to add it here as well:

imports: [
FormsModule,
ReactiveFormsModule
]

Now your Angular components are ready to accept reactive forms approach.

Secondly, generate a component of your own naming and add the following imports:

import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { CustomValidator } from './shared/validation';

The first import itself is pretty self-explanatory. However, the second import CustomValidator is a custom TypeScript file, which consists of our custom validation.

Thirdly, declare a form name in your component like so:

formName: FormGroup;

Call FormBuilder in your component’s constructor as well:

constructor(private fb: FormBuilder) { }

Now the form is ready to be initiated. The following example will throw an alert if the input numberInput is empty and it will accept numbers only, meaning that you will also get an alert if you type characters that are not numbers.

ngOnInit() {
this.formName = this.fb.group({
numberInput: ['', [Validators.required, CustomValidator.numberValidator]]
});
}

Last but not least, let’s add an html part of the component:

<form (ngSubmit)="submit(formName)" [formGroup]="formName">
<div class="form-group">
<div class="row">
<div class="col-md-12">
<input formControlName="numberInput" type="text">
</div>
</div>
</div>
   <small class="form-text text-muted" *ngIf="formName.get('numberInput').touched && formName.get('numberInput').hasError('required')">This field is required!</small>
   <small class="form-text text-muted" *ngIf="formName.get('numberInput').touched && formName.get('numberInput').hasError('invalidNumber')">Numbers accepted only!</small>
   <button type="submit" class="btn btn-primary" [disabled]="formName.invalid">Submit</button>
</form>

Our form is now ready to accept Angular’s built-in validation as well as a validation of our own making. The first alert throws a built-in Angular validation, while the second one is in charge of our custom validation.

Custom Validation Examples

As mentioned in the previous paragraph, here is a custom file which consists of our custom validation examples such as URL, phone number, US zip code, password, US social security number, etc.

I prefer to name this file validation, but you can name it whatever you deem fit.

export class CustomValidator {
// Validates URL
static urlValidator(url): any {
if (url.pristine) {
return null;
}
   const URL_REGEXP = /^(http?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
   url.markAsTouched();
   if (URL_REGEXP.test(url.value)) {
return null;
}
   return {
invalidUrl: true
};
}
// Validates passwords
static matchPassword(group): any {
const password = group.controls.password;
const confirm = group.controls.confirm;
   if (password.pristine || confirm.pristine) {
return null;
}
   group.markAsTouched();
   if (password.value === confirm.value) {
return null;
}
   return {
invalidPassword: true
};
}
// Validates numbers
static numberValidator(number): any {
if (number.pristine) {
return null;
}
   const NUMBER_REGEXP = /^-?[\d.]+(?:e-?\d+)?$/;
   number.markAsTouched();
   if (NUMBER_REGEXP.test(number.value)) {
return null;
}
   return {
invalidNumber: true
};
}
// Validates US SSN
static ssnValidator(ssn): any {
if (ssn.pristine) {
return null;
}
   const SSN_REGEXP = /^(?!219-09-9999|078-05-1120)(?!666|000|9\d{2})\d{3}-(?!00)\d{2}-(?!0{4})\d{4}$/;
   ssn.markAsTouched();
   if (SSN_REGEXP.test(ssn.value)) {
return null;
}
   return {
invalidSsn: true
};
}
// Validates US phone numbers
static phoneValidator(number): any {
if (number.pristine) {
return null;
}
   const PHONE_REGEXP = /^(\([0-9]{3}\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}$/;
   number.markAsTouched();
   if (PHONE_REGEXP.test(number.value)) {
return null;
}
   return {
invalidNumber: true
};
}
// Validates zip codes
static zipCodeValidator(zip): any {
if (zip.pristine) {
return null;
}
   const ZIP_REGEXP = /^[0-9]{5}(?:-[0-9]{4})?$/;
   zip.markAsTouched();
   if (ZIP_REGEXP.test(zip.value)) {
return null;
}
   return {
invalidZip: true
};
}
}

Conclusion

So that was the power of regular expressions utilized the Angular way. Please note that the code published in this article uses JavaScript ES6 as well as TypeScript. It also assumes that you are an intermediate to advanced Angular developer.