Могу ли я программно применять директивы валидации w50 в пользовательской директиве?

Я нашел много примеров следующего шаблона для входов 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!");
    }
}

Я не хочу избавляться от всех директив, основанных на атрибутах, но предпочтительно создавать директиву "macro", которая будет вызывать мой код листинга 1, который будет использоваться для вызова нескольких более "микро" проверок.

Ответ 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);
    }
  };
});

Демо

Ответ 2

Если вы используете больше проверок, вы можете создать службу, которая отвечает за идентификацию и проверку элементов без каких-либо ограничений. Директивы по умолчанию angular остаются.

Пример:

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

Демо

Ответ 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);
         }
       });
      }
    }
  }
})

Ответ 4

Вы можете создать новый компонент, который включает элемент управления со всеми необходимыми валидаторами. Ваш компонент будет выглядеть так:

<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-модели к вашему компоненту для ввода:

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

Вот демон когда вы видите этот компонент в действии и как он работает.

Ответ 5

Вы идете в обратном направлении, потому что вы предполагаете, что директивы настолько трудоемки, что они поддерживают, и хотят, чтобы они давали всю необходимую валидацию в зависимости от элемента.

Это интересный подход, но вы должны быть предупреждены о модульности этого подхода: отдайте эту большую часть труда одной директиве, но это не противоречит лучшим методам "чистого" способа w500, чтобы делать что-то.

Если вы хотите продолжить эту идею, я предлагаю вам просмотреть свойства ngModelController (AngularJS Docs), которые могут быть вводится в функцию link() одной директивы. Точнее, $validators.

Вы можете добавить, сколько $validators к контроллеру NgModel вы хотите.

Во время проверки вы можете установить/отключить действительность для элемента, возвращающего логическое значение:

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 на Angular над управляемым элементом.

Изменить 1:

Как я уже упоминал в комментариях, здесь Plunkr с рабочим примером.