I need to create a directive myDirective which reads all ng-message keys within it and it still creates a valid ng-messages element:
Directive template:
<div id="myDirective">
<div ng-messages="myform.$error" ng-transclude></div>
</div>
Usage:
<my-directive>
<div ng-message="required">something is required</div>
<div ng-message="custom">custom validation</div>
</my-directive>
So in this case I'd like to get an array of "required" and "custom". The problem is I cannot do that since all ng-messsage elements are already hidden (removed from the DOM to be more accurate) by the time my directive gets into its own linking function. How to get around this?
The contents of the directive are removed during the compile phase because your directive has transclude: true
(I'm assuming so, since you are using ng-transclude
).
In this case, the contents are available via the passed-in transclude
function (this is actually what ng-transclude
uses under the covers).
transclude
function is the 5th parameter to the link
(or pre-link) function:
link: function(scope, element, attrs, ctrls, transclude){
var transcludedContents; // jqLite of the contents
transclude(function(clone){
transcludedContents = clone;
});
// do whatever you do to extract ng-message values from transcludedContents
// just remember, that it could also come in the form:
// <ng-message when="required"> and
// <div ng-message-exp="errorMessage.type">
}
Possibly the lazier among us would not want to deal with DOM counting, and so you could create another handler for ng-message
directive that interacts with your my-directive
to register itself:
.directive("myDirective", function() {
return {
transclude: true,
templateUrl: "myDirective.template.html",
controller: function() {
var messages = [];
this.registerNgMessage = function(val) {
messages.push(val);
};
}
};
})
.directive("ngMessage", complementaryNgMessageDirective)
.directive("ngMessageExp", complementaryNgMessageDirective);
function complementaryNgMessageDirective() {
return {
priority: 10, // must be higher than ngMessage, because ngMessage is terminal
require: "^?myDirective", // must be optional not to break existing code
link: function(scope, element, attrs, myDirectiveCtrl) {
if (!myDirectiveCtrl) return; // do nothing, if not paired with myDirective
var staticExp = attrs.ngMessage || attrs.when;
var dynamicExp = attrs.ngMessageExp || attrs.whenExp;
var val;
if (dynamicExp) {
val = scope.$eval(dynamicExp);
} else {
val = staticExp;
}
myDirectiveCtrl.registerNgMessage(val);
}
};
}