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

时间:2015-05-10 09:46:13

标签: javascript angularjs

我发现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代码,该代码将实习调用一组更“微观”的验证。

5 个答案:

答案 0 :(得分:19)

实现此目的的一种方法(即应用现有验证器而不再重复编写代码)将添加验证指令的相应属性并强制重新编译。这将要求您的指令具有足够高的优先级,并且也是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);
    }
  };
});

<强> Demo

答案 1 :(得分:6)

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

示例:

Context ctx = Context.enter();
ctx.getWrapFactory().setJavaPrimitiveWrap(false);

<强> Demo

答案 2 :(得分:3)

您可以尝试这种方法:

.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);
         }
       });
      }
    }
  }
})

答案 3 :(得分:1)

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

<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);
           });
       }
   };
});

以下是demo,您可以看到此组件的运行及其运作方式。

答案 4 :(得分:1)

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

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

如果您想继续这个想法,我建议您查看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一个有效的例子。