Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debounce on only one validator

I've just read the awesome angular page on form validation and I've probably missed something but how can I apply ng-model-options debounce property on a specific validator.

Let me explain the problem. I have a form that validate a public key and for that I have a directive called key-check that contains multiple validator. Some of them are local and synchronous like the format of the key and there is another asynchronous that check if the key is available on the server (asynchronously).

I don't want my server to be flood nor the angular application to be slowed so I use that the cool kids call debouncing and my input seems something like :

<input type="text" ... ng-model="key" key-check ng-model-options="{ debounce: 700 }" ng-minlength="5" ng-maxlength="50"/>

And the directive is like :

ctrl.$validators.keyFormatCheck = function(modelValue) {
   // return boolean
}

ctrl.$asyncValidators.KeyAvailabilityCheck = function(modelValue) {
   // return promise
}

It's work like a charm but all the check are done with 700ms latency and I wonder if it's possible to do the keyFormatCheck without debouncing and the KeyAvailabilityCheck with it. I can probably use the old good way with $timeout but I prefer to do it the angular way.

Any ideas ?

like image 329
Thomas Leduc Avatar asked Oct 12 '15 19:10

Thomas Leduc


1 Answers

ngModelController's debounce applies to the entire pipeline of parsers, validators and view-change-listeners (e.g. ng-change).

There is no way today (Angular v1.4) with ngModelOptions to isolate the debounce delay to a specific validator.

But the functionality is easily achieved by passing a delay parameter to your own async validator directive:

<input ng-model="val" foo-validator="{delay: 500}">

You implement the actual delay with $timeout; no reason here to avoid using $timeout on the grounds that it is somehow less than "the Angular way"; it's not.

The only trick is to make sure that you cancel the previous pending validation request before executing a new one.

var pendingValidation;
ngModel.$asyncValidators.foo = function(modelValue, viewValue){

  if (pendingValidation) $timeout.cancel(pendingValidation);

  pendingValidation = $timeout(function(){
    pendingValidation = null;

    // returns promise or value
    return doAsyncValidation();
  }, delay);

  return pendingValidation;
};

delay can be obtained from a parameter via a foo-validator attribute, either with isolate scope binding (scope: {config: "=fooValidator"}), or by directly using $eval-ing the attribute expression against the right scope:

var config = $scope.$eval(attrs.fooValidator);
var delay = config.delay;
like image 169
New Dev Avatar answered Oct 18 '22 01:10

New Dev