Angular – Return Multiple Results from Custom Validator

Angular Custom Validator

This is a quick addition to the knowledge base, answering: how do I return multiple validation results on the same control from my custom validator? For supporting documentation on how to add custom validators to reactive and template driven forms, see the official Angular documentation @ https://angular.io/guide/form-validation.

Applying the Angular Custom Validator

To associate a custom validator with a FormControl in a reactive form, you might do the following in your component:

productForm: FormGroup;
productkey: AbstractControl;
constructor(fb: FormBuilder) {
    /*
     * apply a required and custom validator to the productkey control.
     */
    this.productForm = fb.group({
        'productkey':['', Validators.compose([Validators.required,
                                              productkeyValidator])],
    });
    this.productkey = this.productForm.controls['productkey'];
}

In the example above, I have a “Product Key” control in my form, and I want to enforce:

  • That it’s required; and
  • That it matches a set of specifications to be validated in my custom validator, productkeyValidator

Defining the Custom Validator

To define my custom validator, I’ll add a function outside of my Component class which returns a StringMap. The signature is as follows:

function productkeyValidator(control: FormControl): {[s:string]: boolean} {

}

The straightforward way to return multiple keys is to create your own StringMap object which is correctly typed per the function’s signature:

function productkeyValidator(control: FormControl): {[s:string]: boolean} {
  let validationMap: new vObj<boolean> = {};
  return validationMap;
}

What is “vObj”?

That is a custom interface I created because I’m not able to instantiate a StringMap object. When I do, I receive:

error TS2304: Cannot find name 'StringMap'

..so my work-around was to create my own interface that matches the documented Validator interface of

{[string: s]: boolean}

Custom Validator – Return Multiple Results

With the map in place, it’s straightforward to return multiple keys from the validator. The validator is going to check and see if the value begins with ‘002’ and ends with ‘XYZ’. If it doesn’t begin with ‘002’, print an error string, and the same if it doesn’t end with ‘XYZ’.

Disclaimer: This example is based on production code that I can’t reasonably refactor for a blog post. So, yes, with a validation as simple as this example, you would certainly just use a single check and print a single message explaining the constraints; e.g. “The Product Key must begin with ‘002’ and end with ‘003’”.

However, in the production case there are 4 significantly distinct validations, and checking-and-printing those validations separately was definitely the favorable user experience versus a single validation explanation to the tune of, “The product key must A, B, C, and D”.

So, we can just populate the map and return:

interface vObj {
    [s: string]: T;
}
function productkeyValidator(control: FormControl): {[s: string]: boolean}  {
  let validationMap: vObj = {};
  if (!control.value.match(/^123/)) {
    validationMap['123'] = true;
  }
  if (!control.value.match(/XYZ$/)) {
    validationMap['XYZ'] = true;
  }
  return validationMap;
}

 

And, to use them in the form:

<div *ngIf="productkey.hasError('123')">
    The Product Key must begin with "123".
</div>
<div *ngIf="productkey.hasError('XYZ')">
    The Product Key must end with "XYZ".
</div>

Form with Validator Keys

 

Thanks for reading.



Categories: Angular, Apps

Tags:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: