我想为我的输入字段创建一个包装器,其中包含一个输入帮助工具提示。
我正在使用 angular 1.0.7,如果它很重要的话。
我正在使用 transclude: true 以及隔离范围,以便同时允许多个不同字段的错误,并且仍然保持对外部 $scope 的 ng-model 引用。
问题:
当我在输入元素上使用此指令时,输入元素不会克隆('Transclude')到指令模板中。
结果,我在 DOM 上得到了一个空的 div 元素,带有一个 ng-transclude 属性。
plunk: 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">
但是,当我将此输入元素包装在跨度或 div 中时,子输入元素被超越就好了,但是我没有在指令中获得对正确外部 ng-model(ctrl) 的引用。
<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);
});
});
}
});