Search code examples
validationangularcustom-validatorsangular-reactive-forms

resuable validator for multiple cases


I made a date validator. It validates existing dates. I need multiple date validator with other restrictions, for ex : a max date validator that won't let the user put in a future date or a date validator that only takes past dates. This is my current validator.

    export function dateValidator(group) {

  const {day, month, year} = group.value;
  const fullDate = `${day}/${month}/${year}`;
  const dobPattern = /^(\d{1,2})[-\/](\d{1,2})[-\/](\d{4})$/;
  const isStringValid = dobPattern.test(fullDate);

  let isValid = false;

  if (isStringValid) {
    const intDay = Number(day);
    const intMonth = Number(month);
    const intYear = Number(year);
    const jsMonth = intMonth - 1;
    const date = new Date(intYear, jsMonth, intDay);
    isValid = (date.getFullYear() === intYear && date.getMonth() === jsMonth && date.getDate() === intDay ;
  }

  return isValid ? null : { invalid: 'Invalid date' };
};

How can I restrict the user from putting in future dates. I used this code with the following line:

    isValid = (date.getFullYear() === intYear && date.getMonth() === jsMonth && date.getDate() === intDay ;

But I wonder if there is an easier way without having to copy and past this code over and over again to make small restrictions to it.


Solution

  • Your dateValidator() function should be a function factory (i.e. a function that returns a function) instead of a function that returns the error directly:

    export function dateValidator(maxDate: string): ValidatorFn {
      // Return a validator function.
      return (group: FormGroup): {[key: string]: any} => {
        // Re-use your existing validation code here and return the error if any.
        // Optionally, use the `maxDate` param to customize the validation:
        // entered dates should not go beyond `maxDate`.
      };
    }
    

    As you can see, you can customize the validator function by passing parameters to the function factory. In my example, I used a maxDate parameter to indicate the furthest date in time that the validator should allow.

    In your form model, use this validator by calling the factory with the appropriate value, e.g. :

    this.myForm = fb.group({
      'date': ['', [Validators.required(), dateValidator('02/20/2017')]]
    });
    

    You can see another example of a function factory for a validator in the doc: https://angular.io/docs/ts/latest/cookbook/form-validation.html#custom-validation