1

I am using Angular 1.3.* and I try to validate my form. It is within a ng-repeat. So I may obtain several input fields. I do not only require the field to be not empty, but also require the search to be successful when retrieving information from the backend.

I have this input field

                               <input type="text"
                               ng-model="user"
                               placeholder="username"
                               name="user{{$index}}"
                               typeahead="data as data.name for data in getUser($viewValue)"
                               typeahead-editable="false"
                               class="form-control"
                               autocomplete="off"
                               userValidator
                               required>
<div ng-if="myForm.$submitted" ng-messages="myForm.user{{$index}}.$error ">
                          <div ng-message="required">You need to type something</div>
                        </div>

                        <div ng-if="myForm.$submitted" ng-messages="myForm.user{{$index}}.$error">
                          <div ng-message="userValidator"> It seems that the entered user is           not a valid input</div>
                        </div>

here is my try for the backendCall

  .directive('userValidator',function($http,$q){
    return{
      require:'ngModel',
      link:function($scope,element,attrs,ngModel){
        ngModel.$asyncValidators.userAvailable=function(userInput){
          return $http.post('/user'+userInput)
            .then(function)????

        }
      }
    }
  });

Note, that I WANT the user to exist. I guess I'll have to make a backend call with asyncValidators. However, I do not seem to get it. A way for me to circumvent this would be to say that on ALL other $errors than "required", show the message "It seems that the entered user is not a valid input". How could I do a successful backend call and use it or alternatively, how can I make sure to show the other error message on all other occasions?

2
  • Shouldn't it be user-validator in your html? Commented Jan 13, 2015 at 13:47
  • you might be right. But how do I have to modify my directive to obtain the right results? Commented Jan 13, 2015 at 13:49

2 Answers 2

9

If the username exists then simply return true, if it does not exists simply reject with the message

.directive('userValidator',function($http,$q){
return{
  require:'ngModel',
  link:function($scope,element,attrs,ngModel){
    ngModel.$asyncValidators.userAvailable = function(modelValue , viewValue) {

     var userInput= modelValue || viewValue;
      return $http.post('/user'+userInput)
         .then(function() {
           //username exists, this means validation success
           return true;
         }, function() {
           //username does not exist, therefore this validation fails
           return $q.reject('selected username does not exists');
         });

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

2 Comments

thenit's no necessary in this case, i'ts redundant, at that point the promise it's already rejected or resolved. In other words, you are resolving a resolved promise an rejecting a rejected promise. However, rejection message it's also unnecessary since validation object only have boolean values.
rejection message is not unnecessary because it can be used for testing. IMO. even though you is right about object only have boolean values. i tried for long time to show error message from server. but no clean way
1

A Firebase example:

.directive('isAccountNameAvailable',[ 'FireRef', '$q', function( FireRef, $q){

return {
  require:'ngModel',
  scope:{
    profile:'=profile'
  },
  link:function($scope, element, attrs, ngModel){
    ngModel.$asyncValidators.isAccountNameAvailable = function(modelValue , viewValue) {
      var deferred  = $q.defer();

      var userInput = modelValue || viewValue;

      $q.all([
          FireRef.child('accountNames').child(userInput).once('value'),
          FireRef.child('users').child($scope.profile.$id).child('accountName').once('value')
        ])
        .then(function(snapshots){

          /*
           snapshots[0].val() is userID in accountNames path e.g accountNames/londonServicesLTD/userID
           snapshots[0].key() is accountName key in accountNames e.g accountNames/londonServicesLTD

           snapshots[1].val() is the current accountName value in user/userID/accountName e.g londonServicesLTD
           snapshots[1].key() is 'accountName' key in user/userID/
          */

          if(!snapshots[0].exists()){
            deferred.resolve(true);
          }else if(snapshots[0].val() === $scope.profile.$id){
            deferred.resolve(true);
          }else{
            deferred.reject(false);
          }

        },function(){
          deferred.reject(false);
        });

      return deferred.promise;
    }
  }
}

}])

html:

   <div class="form-group" ng-class="{'has-success has-feedback': (forms.profileDetails.$submitted && forms.profileDetails.accountName.$valid),'has-error has-feedback': (forms.profileDetails.$submitted && forms.profileDetails.accountName.$invalid) }">
    <label class="control-label">Account Name</label>
    <input type="text" name="accountName" ng-model="model.accountName" required ng-trim no-special-chars camel-case is-account-name-available profile="profile" class="form-control" placeholder="E.g. londonServicesLTD">
    <span ng-show="forms.profileDetails.$submitted" class="glyphicon form-control-feedback" ng-class="{'glyphicon-ok': (forms.profileDetails.accountName.$valid),'glyphicon-remove': ( forms.profileDetails.accountName.$invalid) }" aria-hidden="true"></span>
    <div data-ng-messages="forms.profileDetails.$submitted && forms.profileDetails.accountName.$error" class="help-block">
      <div data-ng-message="required">
        - The <b>account name</b> is required.
      </div>
      <div data-ng-message="noSpecialChars">
        - Not allowed to use special characters.
      </div>
      <div data-ng-message="isAccountNameAvailable">
        - The account name is not available.
      </div>
    </div>
  </div>

1 Comment

You don't need $q,defer here. You can replace deferred.reject with $q.reject and deferred.resolve with true. $q.all already returns a promise, you're duplicating it unnecessarily.

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.