Saturday 2 February 2019

Angular reactive form custom validator with parameter

Angular reactive form custom validator with parameter


In our previous topic we discussed creating a custom email domain validator. The following is that validator function. 

function emailDomain(control: AbstractControl): { [key: string]: any } | null {
  const email: string = control.value;
  const domain = email.substring(email.lastIndexOf('@') + 1);
  if (email === '' || domain.toLowerCase() === 'pragimtech.com') {
    return null;
  } else {
    return { 'emailDomain'true };
  }
}

Notice, the domain name 'pragimtech.com' is hard coded. So this custom validator, only works if you want to check if the domain is pragimtech.com. What if you want to check another domain like microsoft.com. We want to make this custom validator reusable with any domain name. We should be able to pass the domain name as a parameter to the emailDomain custom validator function. 

Notice in the example below, we are passing pragimtech.com as the domain name. If you want to check for a different domain, you simply pass that domain name as a parameter. 

email: ['', [emailDomain('pragimtech.com')]]

The following built-in validators have parameters. 
  • min
  • max
  • minlength
  • maxlength
Notice the min() built-in validator function. It takes in a number as a parameter and returns ValidatorFn. 

min(min: number): ValidatorFn;

So, what is ValidatorFn?
ValidatorFn stands from validator function. So this min() function is taking in a number as a parameter and returns a validator function. If you understand the concept of closure in JavaScript, then this is very easy to understand. We discussed closures in detail in Parts 27 and 28 of JavaScript tutorial.

In simple terms, you can thinks of a closure as, a function inside another function i.e an inner function and an outer function. The inner function has access to the outer function’s variables and parameters in addition to it's own variables and parameters.

Now that task at hand for us, is to convert our emailDomain() function to take in the domain name as a parameter and return a validator function. To be able to do this we are going to take the advantage of closures in JavaScript.

ValidatorFn is an interface and the signature of the function it returns is as shown below. It takes the AbstractControl that we want to validate as an input parameter and returns null or ValidationErrors object. Null if the validation succeeds and a ValidationErrors object is the validation has failed. 

(c: AbstractControl): ValidationErrors null;

Custom Validator with parameter 

function emailDomain(domainName: string) {
  return (control: AbstractControl): { [key: string]: any } | null => {
    const email: string = control.value;
    const domain = email.substring(email.lastIndexOf('@') + 1);
    if (email === '' || domain.toLowerCase() === domainName.toLowerCase()) {
      return null;
    } else {
      return { 'emailDomain'true };
    }
  };
}

Code Explanation 
  • We have 2 functions here. An inner function and an outer function.
  • The outer function has a name (emailDomain), but the inner function does not have a name. It is an anonymous function.
  • The inner anonymous function has access to the outer function parameter domainName.
  • You can have as many parameters as you want in the outer function, then inner function will have access to all of them in addition to it's own parameters.
Passing the value for the custom validator parameter 

email: ['', [Validators.required, emailDomain('dell.com')]]

Finally do not forget to updated the validation error message in the validationMessages structure 

validationMessages = {
  'email': {
    'required''Email is required.',
    'emailDomain''Email domian should be dell.com'
  },
  'proficiency': {
    'required''Proficiency is required.',
  },
};

No comments:

Post a Comment