最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Directive has no access to transcluded elements? - Stack Overflow

programmeradmin0浏览0评论

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 badges
Add a ment  | 

3 Answers 3

Reset to default 6

Please 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 :)

发布评论

评论列表(0)

  1. 暂无评论