AngularJS:在不使用容器的情况下将单个输入元素转换为指令模板

时间:2013-10-07 08:58:26

标签: angularjs angularjs-directive angularjs-scope transclusion

我想为输入字段创建一个包装器,其中包含一个输入帮助工具提示。

我正在使用角度1.0.7,如果它很重要。

我正在使用transclude:true,以及isolate范围,以便同时允许多个不同字段的错误,并仍然保持对外部$ scope的ng-model引用。

问题

当我在input元素上使用this指令时,input元素不会克隆('Transclude')到指令模板中。

因此我在DOM处获得了一个空的div元素,并带有ng-transclude属性。

普拉克: http://plnkr.co/edit/vFB9ih6x2NBmwhAes3Qh?p=preview

代码:

<input data-my-validate-input data-value-required="true" type="password" class="loginItem" placeholder="Password" name="password" data-ng-model="formData.password" data-display-name="Password">

但是当我将这个输入元素包装在span或div中时,子输入元素被超越了,但是我没有在指令处获得对正确的外部ng-model(ctrl)的引用。 / p>

<span data-my-validate-input data-value-required="true" data-display-name="Password">
      <input type="password" class="loginItem" placeholder="Password" name="password" data-ng-model="formData.password" >    
</span>

完整代码(链接功能中的逻辑与问题无关,但我更喜欢发布我的完整代码)

directive('myValidateInput', function() {
    return {
        require: 'ngModel',
          restrict: 'A',
          transclude: true,
          scope: {
            displayName: '@',
            valueRequired: '@',
            maxLength: '@',
            minLength: '@',
            minLetters: '@',
            minNumbers: '@'
          },
          template: '<div class="validationContainer">\
                      <div ng-transclude></div>\
                      <div class="input-help">\
                        <h4>{{fieldErrorDisplay}}</h4>\
                        <ul>\
                          <li data-ng-repeat="rule in requirementSpec" ng-class="rule.class">\
                              {{rule.msg}}\
                          </li>\
                        </ul>\
                      </div>\
                    </div>',
         replace: true,
         link: function(scope, elm, attrs, ctrl) {
                 var validator = function(viewValue){
                          if(scope.valueRequired && viewValue.length == 0 && (!scope.maxLength && !scope.minLength && !scope.minLetters && !scope.minNumbers)){
                    scope.valid = false;  
                    scope.fieldErrorDisplay = scope.fieldName + ' is required';
                  }
                  else{
                        scope.fieldErrorDisplay = scope.fieldName + ' must meet the following requirements: ';
                        scope.requirementSpec = [];
                        if(scope.minLength){
                          var itemValidity = viewValue.length >= scope.minLength;
                          scope.valid = !itemValidity ? false : scope.valid;
                          var item = {
                            'msg' : 'Must be at least ' + scope.minLength + ' characters long',
                            'class' : itemValidity ? 'valid' : undefined
                          };
                          scope.requirementSpec[nameStr].push(item);
                        }
                        else if(scope.valueRequired){
                          var itemValidity = viewValue && viewValue.length >= 1;
                          scope.valid = !itemValidity ? false : scope.valid;
                          var item = {
                            'msg' : 'This field must be filled',
                            'class' : itemValidity ? 'valid' : undefined
                          };
                          scope.requirementSpec[nameStr].push(item);
                        }
                        if(scope.maxLength){
                          var itemValidity = viewValue.length <= scope.maxLength;
                          scope.valid = !itemValidity ? false : scope.valid;
                          var item = {
                            'msg' : 'Must be ' + scope.maxLength + ' characters long at most  ',
                            'class' : itemValidity ? 'valid' : undefined
                          };
                          scope.requirementSpec[nameStr].push(item);
                        }
                        if(scope.minLetters){
                          var itemValidity = (viewValue && /[A-z]/.test(viewValue));
                          scope.valid = !itemValidity ? false : scope.valid;
                          var item = {
                            'msg' : 'Must contain at least ' + scope.minLetters + ' letters',
                            'class' : itemValidity ? 'valid' : undefined
                          };
                          scope.requirementSpec[nameStr].push(item);
                        }
                        if(attrs.minNumbers){
                          var itemValidity = (viewValue && /\d/.test(viewValue));
                          scope.valid = !itemValidity ? false : scope.valid;
                          var item = {
                            'msg' : 'Must contain at least' + attrs.minNumbers + ' numbers',
                            'class' : itemValidity ? 'valid' : undefined
                          };
                          scope.requirementSpec[nameStr].push(item);
                        }
                  }

                  if(scope.valid) {
                      ctrl.$setValidity(nameStr, true);
                      return viewValue;
                  } else {
                      ctrl.$setValidity(nameStr, false);                    
                      return undefined;
                  }
             }

             scope.requirementSpec = {};

             ctrl.$parsers.unshift(function(viewValue) {
               return validator(viewValue);
             });
             ctrl.$formatters.unshift(function(viewValue) {
               // var before = scope.$eval(attrs.validateBefore);
               if(viewValue && viewValue != "" && viewValue.length > 0)
                 return validator(viewValue);

             });


        });
    }
});

1 个答案:

答案 0 :(得分:3)

解决方案: $ transclude只接受元素的编译内容,因此不是它自己的元素。

显然,我在实施中缺乏对这一重要细节的理解。