Victor Queiroz

AngularJS 1.3: Getting to Know Async Validators

What are $asyncValidators?

The $asyncValidators, as the name suggests, are asynchronous validators — they are not executed by Angular independently. They are one of the components of the NgModelController, which is the controller for the most widely used directive in the AngularJS world: ngModel.

Where should I use an async validator?

You should use async validators together with ngModel (obviously), on a text field where a username, an email address, etc. needs to be entered.

How does it work?

Inside each validator, there must be a promise that should return either a resolve or a reject. If it returns resolved, the field will be marked as valid; if it returns rejected, the field will be marked as invalid.

When the validator is marked as invalid, a new class will be added called ng-(denormalized-name-of-your-validator)-invalid, or with the suffix -valid if it’s marked as valid.

While your form has any pending validator, the corresponding FormController will receive the $pending key, as will the corresponding field within the FormController (e.g., form.email.$pending or form.$pending).

The field will only be marked as valid or invalid once all async validators have been resolved. Until then, the $pending key will continue to exist on both the field and the form.

You can also check if your field is valid or invalid through a key inside the $error object, found within the current FormController. Example:

<form name="myForm">
  <div ng-show="myForm.$pending">
    Checking...
  </div>
  <input ng-model="user.email" unique-email>
  <div ng-show="myForm.email.$pending.uniqueEmail">
    Checking...
  </div>
  <div ng-show="myForm.email.$error.uniqueEmail">
    This email is already being used by someone else.
  </div>
</form>

This way, you can implement multiple async validators within a single NgModelController. See the next section to understand this better.

How to use it?

You need to learn how to use the require option of $compile, which will import the NgModelController from the current directive so you can add your async validator. Your validator will be defined by a new key in NgModelController.$asyncValidators — the key should be your validator’s name.

Example

.directive('uniqueUsername', function ($http, $q) {
   return {
     require: '?ngModel',
     link: function (scope, element, attrs, ngModel) {
       ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
         var value = modelValue || viewValue;

         // Look up a user by username
         return $http.get('/api/users/' + value).then(function resolved() {
           return $q.reject('exists');
         }, function rejected() {
           return true;
         });
       };
     }
  };
});

I hope you enjoyed it. Any questions, leave your comments!

Comments