簡體   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