Reputation: 544
Dynamically add the ng-bind attribute through a custom directive to be able to use ng-bind, ng-bind-html or ng-bind-html-unsafe in a custom directive with out manually adding to the template definition everywhere.
http://jsfiddle.net/nstuart/hUxp7/2/
angular.module('app').directive('bindTest', [
'$compile',
function ($compile) {
return {
restrict: 'A',
scope: true,
compile: function (tElem, tAttrs) {
if (!tElem.attr('ng-bind')) {
tElem.attr('ng-bind', 'content');
$compile(tElem)
}
return function (scope, elem, attrs) {
console.log('Linking...');
scope.content = "Content!";
};
}
};
}]);
No idea. Really I can not figure out why something like the above fiddle doesn't work. Tried it with and with out the extra $compile in there.
I can work around it might adding a template value in the directive, but that wraps the content in an extra div, and I would like to be able to that if possible. (See fiddle)
See the fiddle here: http://jsfiddle.net/nstuart/hUxp7/4/ (as suggested by Dr. Ikarus below). I'm considering this a workaround for right now, because it still feels like you should be able to modify the template before you get to the linking function and the changes should be found/applied.
Upvotes: 3
Views: 3857
Reputation: 31580
You guys were so close.
function MyDirective($compile) {
function compileMyDirective(tElement) {
tElement.attr('ng-bind', 'someScopeProp');
return postLinkMyDirective;
}
function postLinkMyDirective(iScope, iElement, iAttrs) {
if (!('ngBind' in iAttrs)) {
// Before $compile is run below, `ng-bind` is just a DOM attribute
// and thus is not in iAttrs yet.
$compile(iElement)(iScope);
}
}
var defObj = {
compile: compileMyDirective,
scope: {
someScopeProp: '=myDirective'
}
};
return defObj;
}
The result will be:
<ANY my-directive="'hello'" ng-bind="someScopeProp">hello</ANY>
Upvotes: 1
Reputation: 6384
This way seems more elegant (no dependency with $compile) and appropriate to your case :
angular.module('app').directive('myCustomDirective', function () {
return {
restrict: 'A',
scope: {},
template: function(tElem, tAttrs) {
return tAttrs['ng-bind'];
},
link: function (scope, elem) {
scope.content = "Happy!";
}
};
});
jsFiddle : http://jsfiddle.net/hUxp7/8/
From Angular directive documentation : You can specify template as a string representing the template or as a function which takes two arguments tElement and tAttrs (described in the compile function api below) and returns a string value representing the template.
Upvotes: 2
Reputation: 16261
The source code tells all! Check out the compileNodes()
function and its use of collectDirectives()
.
First, collectDirectives
finds all the directives on a single node. After we've collected all the directives on that node, then the directives are applied to the node.
So when your compile
function on the bindTest
directive executes, the running $compile()
is past the point of collecting the directives to compile.
The extra call to $compile
in your bindTest
directive won't work because you are not linking the directive to the $scope
. You don't have access to the $scope
in the compile
function, but you can use the same strategy in a link
function where you do have access to the $scope
Upvotes: 1
Reputation: 6784
You could do the compiling part inside the linking function, like this:
angular.module('app').directive('bindTest', ['$compile', function ($compile) {
return {
restrict: 'A',
scope: true,
link: {
post: function(scope, element, attrs){
if (!element.attr('ng-bind')) {
element.attr('ng-bind', 'content');
var compiledElement = $compile(element)(scope);
}
console.log('Linking...');
scope.content = "Content!";
}
}
};
}]);
Let me know how well this worked for you http://jsfiddle.net/bPCFj/
Upvotes: 2