Вызов асинхронной службы в директиве пользовательской проверки AngularJS

#angularjs #validation #angularjs-directive

#angularjs #проверка #angularjs-директива

Вопрос:

У меня есть директива для пользовательской проверки (убедитесь, что имя пользователя еще не существует). Проверка использует службу $ http, чтобы запросить сервер, существует ли имя пользователя, поэтому возвращаемый объект является объектом promise . Это работает фантастически для проверки. Форма недопустима и содержит myform.$error.usernameVerify, когда имя пользователя уже занято. Однако user.username всегда не определен, поэтому он нарушает мою директиву ng-model. Я думаю, это, вероятно, потому, что функция в .success создает собственную область видимости, а возвращаемое значение не используется в $scope контроллеров. Как мне это исправить, чтобы привязка к ng-модели все еще работала?

 commonModule.directive("usernameVerify", [
    'userSvc', function(userSvc) {
        return {
            require: 'ngModel',
            scope: false,
            link: function(scope, element, attrs, ctrl) {
                ctrl.$parsers.unshift(checkForAvailability);
                ctrl.$formatters.unshift(checkForAvailability);

                function checkForAvailability(value) {
                    if (value.length < 5) {
                        return value;
                    }
                    // the userSvc.userExists function is just a call to a rest api using $http
                    userSvc.userExists(value)
                        .success(function(alreadyUsed) {
                            var valid = alreadyUsed === 'false';
                            if (valid) {
                                ctrl.$setValidity('usernameVerify', true);
                                return value;
                            } 
                            ctrl.$setValidity('usernameVerify', false);
                            return undefined;
                        });
                }
            }
        }
    }
]);
  

Вот мой шаблон:

 <div class="form-group" ng-class="{'has-error': accountForm.username.$dirty amp;amp; accountForm.username.$invalid}">
    <label class=" col-md-3 control-label">Username:</label>
    <div class="col-md-9">
        <input name="username"
               type="text"
               class="form-control"
               ng-model="user.username"
               ng-disabled="user.id"
               ng-minlength=5
               username-verify
               required />
        <span class="field-validation-error" ng-show="accountForm.username.$dirty amp;amp; accountForm.username.$error.required">Username is required.</span>
        <span class="field-validation-error" ng-show="accountForm.username.$dirty amp;amp; accountForm.username.$error.minlength">Username must be at least 5 characters.</span>
        <span class="field-validation-error" ng-show="accountForm.username.$dirty amp;amp; accountForm.username.$error.usernameVerify">Username already taken.</span>
    </div>
</div>
  

Ответ №1:

Angular имеет выделенный массив $asyncValidators именно для этой ситуации:

см. https://docs.angularjs.org/api/ng/type/ngModel .ngModelController

 ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
var value = modelValue || viewValue;

// Lookup user by username
return $http.get({url:'/api/users/'   value}).
   then(function resolved() {
     //username exists, this means validation fails
     return $q.reject('exists');
   }, function rejected() {
     //username does not exist, therefore this validation passes
     return true;
   });
};
  

Комментарии:

1. Примечание: Это доступно с Angular 1.3.0

Ответ №2:

Чтобы заставить это работать, мне нужно было добавить «возвращаемое значение;» вне асинхронного вызова. Код ниже.

 commonModule.directive("usernameVerify", [
    'userSvc', function(userSvc) {
        return {
            require: 'ngModel',
            scope: false,
            link: function(scope, element, attrs, ctrl) {
                ctrl.$parsers.unshift(checkForAvailability);
                ctrl.$formatters.unshift(checkForAvailability);

                function checkForAvailability(value) {
                    if (value.length < 5) {
                        return value;
                    }
                    userSvc.userExists(value)
                        .success(function(alreadyUsed) {
                            var valid = alreadyUsed === 'false';
                            if (valid) {
                                ctrl.$setValidity('usernameVerify', true);
                                return value;
                            }
                            ctrl.$setValidity('usernameVerify', false);
                            return undefined;
                        });
                    // Below is the added line of code.
                    return value;
                }
            }
        }
    }
]);
  

Комментарии:

1. Это неверно. Проверьте blog.brunoscopelliti.com/form-validation-the-angularjs-way

2. Проблема с этим подходом заключается в том, что функция checkForAvailability немедленно вернет (с вашим значением), поскольку функции userExists возвращают обещание, которое будет выполнено позже.