为什么添加额外的 AngularJS 验证指令会导致 $async Validators 多次 运行?

Why does adding additional AngularJS validation directives cause $asyncValidators to run multiple times?

为什么添加额外的 AngularJS 验证指令会导致页面加载时 $asyncValidators 到 运行 多次?

我创建了一个实现 $asyncValidators 的自定义指令。这是该自定义指令的基本结构:

myApp.directive('userSaved',['$q','userLookup',function($q, userLookup){
return {
  restrict: 'A',
  require: 'ngModel',
  link: function(scope, elem, attrs, ctrl){
    ctrl.$asyncValidators.userSaved = function(modelValue, viewValue) {
      // do stuff 
    }
  }
}
}]);

控制器像这样初始化 tailNumber 模型值:

$scope.tailNumber = 'N33221';

这是 html,其中 user-saved 指令在页面加载时 运行s 3 次

<input ng-model="tailNumber" name="tailNumber"   user-saved 
    ng-minlength="2"   ng-pattern="/^[A-z][a-zA-Z0-9]*$/" >

当我删除 ng-minlength="2" 时,user-saved 指令在页面加载时 运行s 两次(2 次)。这是删除了 ng-minlength="2" 的 html:

<input ng-model="tailNumber" name="tailNumber"   user-saved 
    ng-pattern="/^[A-z][a-zA-Z0-9]*$/" >

当我删除 ng-pattern="/^[A-z][a-zA-Z0-9]*$/" 时,user-saved 指令只有 运行s 1 次 。这是删除 ng-pattern="/^[A-z][a-zA-Z0-9]*$/"

后的 html
<input ng-model="tailNumber" name="tailNumber" user-saved >

为什么我的函数向 $asyncValidators 运行 注册附加到表单元素的每个附加 ng 验证器需要额外的时间?

我的自定义指令是一个昂贵的 $http 调用,我更喜欢我的自定义指令在页面加载时只 运行 一次。是否可以使用所有这些 ng 验证器并且在页面加载时只 运行 宁我的异步验证器函数一次而不是 3 次?

这是因为 ngMaxlengthngPattern 等验证指令通过调用 ngModelController.$validate().

来调用初始验证周期

这会导致所有验证指令运行它们的验证逻辑,包括异步验证器。

防止冗余 $http 调用的一种方法是缓存每个输入的验证结果。

实际上我花了一段时间才弄明白这一点。如 this post 中所述,Angular 验证器会触发额外的验证。我决定不反对这种行为,而是解决它,转而使用解析器和格式化程序:

myApp.directive('userSaved',['$q','dataservice',function($q, dataservice){
return {
  restrict: 'A',
  require: 'ngModel',
  link: function(scope, elem, attrs, ctrl){
    ctrl.$parsers.unshift(checkUserSaved);
    ctrl.$formatters.unshift(checkUserSaved);

    function checkUserSaved(value){
        ctrl.$setValidity("usersaved") // the absence of the state parameter sets $pending to true for this validation
        dataservice.getUserSaved(value).then(function(response){
            var userIsSaved = (response === true);

            ctrl.$setValidity("usersaved", userIsSaved); // the presence of the state parameter removes $pending for this validation

            return userIsSaved ? value : undefined;
        });

        return value;
    }
  }
}
}]);

作为参考,您可能还想查看 Angular docs

编辑

经进一步调查,似乎在 ng-pattern 的情况下,仅当正则表达式从字符串转换时才会触发额外的验证。

直接传递正则表达式:

<div ng-pattern="/^[0-9]$/" user-saved></div> 

解决了我在使用验证器管道时遇到的问题。

参考 this github issue

我听从了@New Dev 的建议并实现了一个简单的缓存例程,它很好地满足了我的要求,这就是我想出的..

link: function (scope, element, attributes, ngModel) {

        var cache = {};
        ngModel.$asyncValidators.validateValue = function (modelValue) {
            if (modelValue && cache[modelValue] !== true) {
                return MyHttpService.validateValue(modelValue).then(function (resolved) {
                    cache[modelValue] = true; // cache 
                    return resolved;
                }, function(rejected) {
                    cache[modelValue] = false;
                    return $q.reject(rejected);
                });
            } else {
                return $q.resolve("OK");
            }
        };
    }