Problem
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.
Example
/
Broken Directive
angular.module('app').directive('bindTest', [
'$pile',
function ($pile) {
return {
restrict: 'A',
scope: true,
pile: function (tElem, tAttrs) {
if (!tElem.attr('ng-bind')) {
tElem.attr('ng-bind', 'content');
$pile(tElem)
}
return function (scope, elem, attrs) {
console.log('Linking...');
scope.content = "Content!";
};
}
};
}]);
Solution
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 $pile in there.
Workaround
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)
Second Workaround
See the fiddle here: / (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.
Problem
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.
Example
http://jsfiddle/nstuart/hUxp7/2/
Broken Directive
angular.module('app').directive('bindTest', [
'$pile',
function ($pile) {
return {
restrict: 'A',
scope: true,
pile: function (tElem, tAttrs) {
if (!tElem.attr('ng-bind')) {
tElem.attr('ng-bind', 'content');
$pile(tElem)
}
return function (scope, elem, attrs) {
console.log('Linking...');
scope.content = "Content!";
};
}
};
}]);
Solution
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 $pile in there.
Workaround
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)
Second Workaround
See the fiddle here: http://jsfiddle/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.
Share Improve this question edited Jun 20, 2020 at 9:12 CommunityBot 11 silver badge asked Mar 11, 2013 at 23:26 nick.stuartnick.stuart 5441 gold badge7 silver badges18 bronze badges4 Answers
Reset to default 2You could do the piling part inside the linking function, like this:
angular.module('app').directive('bindTest', ['$pile', function ($pile) {
return {
restrict: 'A',
scope: true,
link: {
post: function(scope, element, attrs){
if (!element.attr('ng-bind')) {
element.attr('ng-bind', 'content');
var piledElement = $pile(element)(scope);
}
console.log('Linking...');
scope.content = "Content!";
}
}
};
}]);
Let me know how well this worked for you http://jsfiddle/bPCFj/
This way seems more elegant (no dependency with $pile) 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/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 pile function api below) and returns a string value representing the template.
The source code tells all! Check out the pileNodes()
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 pile
function on the bindTest
directive executes, the running $pile()
is past the point of collecting the directives to pile.
The extra call to $pile
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 pile
function, but you can use the same strategy in a link
function where you do have access to the $scope
You guys were so close.
function MyDirective($pile) {
function pileMyDirective(tElement) {
tElement.attr('ng-bind', 'someScopeProp');
return postLinkMyDirective;
}
function postLinkMyDirective(iScope, iElement, iAttrs) {
if (!('ngBind' in iAttrs)) {
// Before $pile is run below, `ng-bind` is just a DOM attribute
// and thus is not in iAttrs yet.
$pile(iElement)(iScope);
}
}
var defObj = {
pile: pileMyDirective,
scope: {
someScopeProp: '=myDirective'
}
};
return defObj;
}
The result will be:
<ANY my-directive="'hello'" ng-bind="someScopeProp">hello</ANY>