I would like to create a wrapper for my input field with an input-help tool tip inside of it.
I am using angular 1.0.7, if it's significant.
I'm using transclude: true, along with isolate scope in order to allow errors at several different fields simultaneously, and still maintain the ng-model reference to the external $scope.
The Problem:
when I use this directive on the input element, the input element doesn't clone('Transclude') into the directive template.
As a result of that I am getting an empty div element at the DOM, with an ng-transclude attribute.
plunk: http://plnkr.co/edit/vFB9ih6x2NBmwhAes3Qh?p=preview
code:
<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">
However when I wrap this input element in a span or div, the child input element is transcended just fine, but then I don't get the a reference to the correct external ng-model(ctrl) at the directive.
<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>
Full code(the logic inside the link function is not relevant to the problem, but I preferred to post my full code)
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);
});
});
}
});
The ng-transclude directive facilitates AngularJS to capture everything that is put inside the directive in the markup and use it somewhere in the directive's template.
Create New Directives In addition to all the built-in AngularJS directives, you can create your own directives. New directives are created by using the . directive function. To invoke the new directive, make an HTML element with the same tag name as the new directive.
Using attrs you are able to access the attributes defined in your html tag like <fm-rating ng-model="$parent.restaurant.price" symbol="$" readonly="true"> So in this case you will have access to the symbol and readonly attributes.
Note: When you create a directive, it is restricted to attribute and elements only by default. In order to create directives that are triggered by class name, you need to use the restrict option. The restrict option is typically set to: 'A' - only matches attribute name. 'E' - only matches element name.
The solution: $transclude only takes the compiled content of the element, thus not the element it self.
Obviously I lack the understanding of this significant detail in my implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With