Search code examples
angularjsangular-resource

Set form/input field invalid from $asyncValidators server serponse


When user enters his email, I send it to server in order to check if it is in use, if I got server response as error I want to set form to invalid and present message "emailInUse"

My directive

ctrl.$asyncValidators.email = function(ctrl, viewValue) {
            return registerResource.emailAvailable.save({"email":viewValue}).$promise.then(
                    function success(response) {
                        // Set the form valid
                    },
                    function error(response) {
                        // Set the form invalid
                    });
        };

My template

 <form name="registerForm" ng-submit="vm.register()" role="form">
    <div class="form-group">
        <div>
            <input type="email"
                   placeholder="email"
                   name="email"
                   class="form-control"
                   ng-model="vm.email"
                   email-available
                   ng-minlength=4 ng-maxlength=50 required/>

            <div ng-messages="registerForm.email.$error">
                <div ng-message="required">
                    You forgot to enter your email address...
                </div>
                <div ng-message="email">
                    You did not enter your email address correctly...
                </div>
                <div ng-message="emailInUse">
                    You did not enter your email address correctly...
                </div>
            </div>
        </div>
    </div>

Solution

  • You need to set up a defered object and resolve or reject it according to your resources success or error case.

    I took the example code from https://docs.angularjs.org/guide/forms and changed it a little bit to match your code. See here:

    app.directive('emailAvailable', function($q, $timeout) {
      return {
        require: 'ngModel',
        link: function(scope, elm, attrs, ctrl) {
    
          ctrl.$asyncValidators.emailInUse = function(modelValue, viewValue) {
    
            var def = $q.defer();
    
            registerResource.emailAvailable.save({"email":viewValue}).$promise.then(
              function success(response) {
                  // Set the form valid
                  def.resolve();
              },
              function error(response) {
                  // Set the form invalid
                  def.reject();
              });
    
            return def.promise;
          };
        }
      };
    });
    

    I have changed Angular's example plnkr to get a better understanding:

    http://plnkr.co/edit/bfSUcqJONz5QAg6vnZ7O?p=preview

    Hint: It uses a $timeout instead of an http-call to a backend. But if you enter one of those names var usernames = ['Jim', 'John', 'Jill', 'Jackie']; it shows you with a small delay of 2 seconds that the username is already taken.