2

I have a form where some of the inputs are hooked up to a custom validator via a directive. The input should validate on blur, and does so via an asynchronous REST API call.

HTML:

<input type="text"
   validate-this
   ng-model="thisField"
   ng-model-options="{'updateOn': 'blur'}"
   ng-pattern="some pattern"
/>

Directive (shortened for brevity):

return {
  access: 'A',
  require: 'ngModel',
  scope: false,
  link: function (scope, elem, attrs, ngModel) {
    ngModel.$asyncValidators.validateThis = function (modelVal, viewVal) {
      if (!modelVal && !viewVal) return $q.defer().promise;

      // returns a promise from the api service
      return api.doSomeValidation();
    };
  }
};

The above code works perfectly, but notice the hackish line directly below the validation function signature:

if (!modelVal && !viewVal) return $q.defer().promise;

Without that line, Angular attempts to validate the field immediately on application load instead of only on blur. This is a problem as the actual validation code does some string parsing, and since both modelVal and viewVal are undefined, JavaScript throws an error.

I have tried disabling the functionality that loads data into the fields when the application loads, and the error still happens. The pattern specified in ng-pattern, however, does respect my wishes and only validates on field blur--it does NOT attempt to validate on page load. Is there any way to tell Angular to only validate on blur, or to get it to stop trying to validate as soon as the page loads? Or am I using $asyncValidators incorrectly?

Mike
  • 4,071
  • 21
  • 36
  • [How to add custom validation to an AngularJS form?](https://stackoverflow.com/q/12581439/6521116) – LF00 Nov 14 '17 at 08:22

1 Answers1

2

Angular executes the $validate during the signal of each attr.$observe for the input validations directives such as ngPattern. You can see that in their patternDirective function.

I have been trying to find a way around this as I use many of the input validations (pattern, max length, required, etc.) and my $asyncValidators are triggering 7 times during load. This caused the web server to execute for each trigger.

Solutions:

  • cache the web method response
  • attach your handler after the page load (or have some type of flag)
  • bake the ngPattern test into your async handler. I would probably go with this one.

    ngModel.$asyncValidators.validateThis = function (modelVal, viewVal) {
        var deferred = $q.defer();
    
        if (!modelVal && !viewVal)
            deferred.resolve();
        else if (!myPattern.test(modelVal))
            deferred.reject();
        else
            api.doSomeValidation(value).then(function (result) {
                deferred.resolve();
            }, function (result) {
                deferred.reject();
            })
    
        return deferred.promise;
    };
    

Hopefully this helps as I am in the same boat looking for a solution.

RickCard
  • 23
  • 3