2

I have a simple HTML5 user registration form. The value of the username input is validated to ensure that there are no users with the same username using a custom function added to $asyncValidators.

Consider the following situation:

  1. User starts typing in a username (e.g., 'Me').
  2. After a slight delay, Angular validators run, sending asynchronous request to server asking if the username is available.
  3. User begins typing more characters (e.g., 'Me123').
  4. After another slight delay, Angular again tries to validate the username by sending another request to server.

What does Angular do when it receives two responses regarding the validity of the username? Is there a way to handle this gracefully?

What I'd like to do is somehow cancel or tell Angular to ignore the result of the first validation attempt as soon as the user modifies the username value.


EDIT 11/12/2015:

sirrocco's answer helped me clarify my question. There are two pieces here:

  1. abort actual HTTP request
  2. tell Angular validation process to stop waiting for the promise returned from $asyncValidators.usernameAvailable to be resolved or rejected

I could do the first by using the timeout option of the $http service. My question is: Is it necessary, desirable, or possible to do the second? If so, how?

I'm thinking this might happen in a a keyup event handler added to the element in the directive link. See below:

index.html

<input 
  ng-model="user.username" 
  ng-model-options="{ debounce: 250 }" 
  check-availability />

checkAvailability.directive.js

function affectsText(keyCode) {
  // some code to determine if keyCode could change value of a text input
}

angular
  .module('app')
  .directive('checkAvailability', ['$q', 'users', function($q, users) {
    return {
      restrict: 'AC',
      require: 'ngModel',
      link: function (scope, element, attrs, ctrl) {
        element.on('keyup', function(e) {
          if (!affectsText(e.keyCode)) {
            return;
          }

          // cancel previous $asyncValidators.usernameAvailable process??

          // users service cancels operation by rejecting a promise previously assigned to to $http's timeout option
          users.cancelIsAvailableCheck();
        });

        ctrl.$asyncValidators.usernameAvailable = function(modelValue) {
          if (ctrl.$isEmpty(modelValue)) {
            return $q.when();
          }

          var deferred = $q.defer();

          users.isAvailable(modelValue).then(function(available) {
            if (available) {
              deferred.resolve();
            } else {
              deferred.reject();
            }
          });

          return deferred.promise;
        }
      }
    };
  }]);
2
  • It seems AngularJS should take care of this. I'm not certain that it does, but it seems it would be the one to deal with this. I'm seeing inconsistent results, however... if the first $http call takes longer than the second call, then the promises (and therefore validation results) are evaluated out of order. Commented Dec 11, 2015 at 14:33
  • @dprothero, yes, I'm getting inconsistent results, too. This plunkr seems to give the desired validation results. However, a very similar scenario in my local dev environment has the opposite result. Commented Dec 11, 2015 at 18:22

1 Answer 1

1

The simplest way would be to just disable the user input like:

<input 
  name="username"
  ng-model="user.username" 
  ng-model-options="{ updateOn: 'blur' }"
  ng-disabled="myForm.$pending"
  check-availability />

So once the user tabs out of the username field, the validation is triggered and the field is disabled (you could also disable to submit button).

If that's not an acceptable UX, you could instead use the method described here : http://www.bennadel.com/blog/2616-aborting-ajax-requests-using-http-and-angularjs.htm

Essentially $http allows for a promise to be set on its timeout property. If you then resolve that property, the http call is aborted. It might take some trial and error to figure out the best approach within the validator.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the link to the excellent article. When I get a chance, I'll play around with that and see if it works for me.
After thinking about this, the timeout option of $http, while related, doesn't solve/answer my actual question. It's probably good to cancel/timeout the actual HTTP request, but I'd also like to abort the Angular validation process. I'll edit the question to make this clear.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.