I might get some concept terribly wrong, but I don't get this to work I expected:
What I'm trying to do is to define a directive that gets attached to a top-level <nav>
element, and then modifies the contained DOM elements in its link function (such as adding css classes to <li>
etc.).
However, the link function seems to only get the original directive template (<nav><ul><ng-transclude/></ul></nav
), and not the transcluded/expanded DOM elements.
If this is "by design", how should I do this? It find it pretty useless to define a transcluding "root" directive, if it does not have access to the transcluded DOM tree....
I might get some concept terribly wrong, but I don't get this to work I expected: http://plnkr.co/edit/Qe2IzMMMR5BJZJpwkx9e?p=preview
What I'm trying to do is to define a directive that gets attached to a top-level <nav>
element, and then modifies the contained DOM elements in its link function (such as adding css classes to <li>
etc.).
However, the link function seems to only get the original directive template (<nav><ul><ng-transclude/></ul></nav
), and not the transcluded/expanded DOM elements.
If this is "by design", how should I do this? It find it pretty useless to define a transcluding "root" directive, if it does not have access to the transcluded DOM tree....
Share Improve this question asked Jan 21, 2014 at 18:02 FlorianFlorian 3113 silver badges14 bronze badges3 Answers
Reset to default 6Please read some of my answers about transclusion in angular:
- What is the main use of transclusion in angularjs
- Why I can't access the right scope?
As to your question:
- First, it's not useless , even if not fit to your use case.
- Yes, it's by design - but it's just the default behavior of
ng-transclude
. - If it was the opposite then everyone would be yelling about scope leaking.
- You can do anything you want with
$transclude
, just be careful. - There are probably better solutions like creating isolated scope with bindings.
This is what you wanted (plunker):
angular.module('app').directive ('myNav', ['$timeout', function($timeout) {
return {
replace: false,
transclude: true,
template: '<nav><ul></ul></nav>',
link: function (scope, element, attrs,ctrl,$translcude){
$transclude(scope,function(clone){
element.find('ul').html(clone)
});
var items = element.find('li'); //element.find('ng-transclude') === 1 !
window.console.log (items.length);
}
};
(Correct answers see above from Ilan and others)
I finally got my (simple) use case working without transclude at all with the old dirty $timeout hack: http://plnkr.co/edit/FEEDYJLK9qRt0F4DNzRr?p=preview
link: function(scope, element) {
// add to end of event queue
$timeout(function() {
var items = element.children('ul:first').children('li');
window.console.log(items.length);
}, 0);
}
I know this is a bad thing to do, and not totally sure if this will work always, but at least seems to work for my simple case...
I think the issue is that you have a ng-repeat within the directive so the "element" is not able to access the child nodes until the ng-repeats have been resolved. A way around this is to have your directive on each of the list tags. I'd add transclude to the tag, and then you can remove the template from your directive all together.
You'd end up with something like:
<li ng-repeat="item in menuItems" my-nav ng-transclude>
Your directive would look like
angular.module('app').directive ('myNav', ['$timeout', function($timeout) {
return {
replace: false,
transclude: true,
pile: function (element, attrs, transclude){
// this will always return 0 unless you split this into two directives
// and emit or watch for the ng-repeats to plete in the parent
// directive
//var items = $(element).find('li'); //element.find('ng-transclude') === 1 !
//instead showing you how to access css for the given element
element.css( "color", "red" );
}
};
}]);
As I mentioned in the ments above, you could split the directive into two directives: one at the nav level and one on your ng-repeat that simply emits when the repeats are done and you can apply css accordingly as the find will then be able to find the child nodes as they are resolved. I think that approach is redundant however, as you'd be setting css for nodes to which you've already applied your change. I think as noted in one of the ments below, smaller directives work better and your project is less likely to bee a transcluded mess of spaghetti like scopes. Happy coding :)