繁体   English   中英

我可以在自定义指令中以编程方式应用Angular验证指令吗?

[英]Can I programmatically apply Angular validation directives inside a custom directive?

我发现html输入有以下几种模式,这适用于电话号码:

<input type="text" ng-model="CellPhoneNumber" required ng-pattern="/^[0-9]+$/" ng-minlength="10" />

我想创建一个自定义指令,无论何时应用,都会告诉Angular应用所有这三个规则,例如:

<input type="text" ng-model="CellPhoneNumber" bk-ng-validation="phoneNumber"/>

然后,我的指令中的代码将找到并调用一个名为phoneNumber的函数,其中我希望看到类似于:

清单1:

function bkNgPhoneNumber(model) {
    // This is purely SPECULATIVE pseudo-code, just to convey an idea.
    model.errors.add(applyMinLength(10, model));
    model.errors.add(applyMaxLength(15, model));
    model.errors.add(applyPattern("/^[0-9]+$/", model));
}

我更喜欢上述方法而不是'为这些规则重写代码,例如:

清单2:

function phoneNumber(model) {
    if (model.length < 10 || model.length > 15) {
        model.errors.add("Must be 10 to 15 chars!");
    }
}

我不想废除所有基于属性的指令,但最好创建一个'宏'指令,它将调用我的清单1代码,该代码将实习调用一组更“微观”的验证。

一种方法(即应用现有验证器而不再重复编写代码)将添加验证指令的相应属性并强制重新编译。 这将要求您的指令具有足够高的优先级,并且也是terminal: true

app.directive("bkNgValidation", function($compile){
  return {
    priority: 10000,
    terminal: true,
    link: function(scope, element){
      element.attr("ng-required", "true");
      element.attr("ng-minlength", 20);
      element.attr("ng-maxlength", 30);

      // prevent infinite loop
      element.removeAttr("bk-ng-validation");

      $compile(element)(scope);
    }
  };
});

演示

如果您使用的是更多验证,则可以创建一个负责识别和验证元素的服务,而不受任何限制。 角度的默认指令保持不变。

例:

    module.service('$Validation', ["$compile",function($compile){

        this.validators = {
            'phoneNumber': [['required', 1], ['minlength',6], ['maxlength', 10], ['pattern', /^[0-9]+$/.source]],
            'phoneNumber2Custom': function(value){ 
                return /^[0-9]{6,10}$/.test(value) 
            },
            'userTwitter': function(value){
                return /^@(.+)/.test(value)
            }
            // ...etc... /
        }

        this.add = function(scope, element, attrs, model){
            var name = attrs.bkNgValidation, type;
            if(!(type = this.validators[name])) return;
            else if(angular.isFunction(type)) return (model.$validators[name] = type);

            element.removeAttr("bk-ng-validation");
            angular.forEach(type, function(expr){
                element.attr(expr[0], expr[1])
            });
            $compile(element)(scope)        
        };

    }]).directive('bkNgValidation', ["$Validation", function ($Validation) {
        return {
            require: '?ngModel',
            priority: 1e5,
            link: function(){
                $Validation.add.apply($Validation, arguments);
            }
        }
    }])

演示

你可以尝试这种方法:

.directive('bkNgValidation', function () {
  return: {
    link: function (scope, element, attrs) {
      if (attrs['bk-ng-validation'] === 'phoneNumber') {
       element.$validateModel(function (value, validator) {
         if (value.length < 10 || value.length > 15) {
           validator.$setValidity('phone', true);
         } else {
           validator.$setValidity('phone', false);
         }
       });
      }
    }
  }
})

您可以创建一个新组件,其中包括对所有必需验证器的控制。 您的组件看起来像:

<my-control name="field" ng-model="text"></my-control>

所有必需的逻辑组件都应保留在内部 为此,使用模板创建my-control指令。 在模板内部,您可以输入带有验证属性的输入:

<input type="text" ng-model="value" class="form-control" ng-pattern="'^(?!ng-*)'" minlength="3">

然后,您需要将组件上的ng-model值绑定到输入:

angular.module('app', []).directive('myControl', function() {
   return {
       restrict: 'E',
       require: 'ngModel', //import ngModel into linking function
       templateUrl: 'myControl.tpl',
       scope: {}, //our component can have private properties, isolate it
       link: function(scope, elm, attrs, ngModel) {
           // reflect model changes via js
           ngModel.$render = function() {
               scope.value = ngModel.$viewValue;
           };
           // update model value after changes in input
           scope.$watch('value', function(value) {
               ngModel.$setViewValue(value);
           });
       }
   };
});

这是一个演示 ,您可以看到此组件的运行及其工作原理。

你正在以相反的方式前进,因为你假设指令是非常费力的维护,并希望保持一个给你所需的所有验证,具体取决于元素。

这是一个有趣的方法,但是你需要警告这种方法的模块性:将这么多的劳动分配给一个指令只是违背了做“纯粹的角度方式”来做事情的最佳实践。

如果你想继续这个想法,我建议你查看ngModelControllerAngularJS Docs )属性,这些属性可以注入一个指令的link()函数。 更准确地说, $validators

您可以向所需的NgModel控制器添加多少$validators

在验证期间,您可以为返回布尔值的元素设置/取消设置有效性:

app.directive('validator', function () {
    var definition = {
        restrict: 'A',
        require: '?ngModel',
        link: function (scope, element, attrs, ngModel) {
            // Return if no ngModelController
            if (!ngModel) {
                return;
            }

            ngModel.$validators.validateLength = function (modelValue, viewValue) {
                // modelValue === the value of the ngModel, on the script side
                // viewValue === the value of the ngModel, on the HTML (rendered) side
                // 
                // you can set-up $parsers, $formatters, $validators, etc, to handle the element

                return !(modelValue.length > 20);
            }
        }
    };

    return definition;
});

我建议你阅读更多关于这个实现的内容,因为某些操作可以在操纵元素上的角度上中断$digest循环的流量。

编辑1:

就像我在评论中提到的那样,这是一个带有工作示例的Plunkr

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM