Angular.js: Wrapping elements in a nested transclude

All we need is an easy explanation of the problem, so here it is.

This seems like such a simple thing, but I am just not able to wrap my head around how to do it.

Here is what I want:

<my-buttons>
  <my-button ng-click="doOneThing()">abc</my-button>
  <my-button ng-click="doAnotherThing()">def</my-button>
</my-buttons>

That turns into something like this:

<ul class="u">
  <li class="l"><button ng-click="doOneThing()">abc</button></li>
  <li class="l"><button ng-click="doAnotherThing()">def</button></li>
</ul>

Notice how the ng-click is on the button, inside a wrapping li. However, the normal transclusion will place the ng-click on the li.

My best try is on this fiddle: http://jsfiddle.net/WTv7k/1/ There I have replaced the ng-click with a class, so it is easy to see when it works and not.

Any ideas of how to get this done? If it is really easy, maybe the tabs/pane example on the frontpage could be expanded to include a wrapper around the panes, while still keeping the attributes.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

With replace:true the replacement process migrates all of the attributes / classes from the old element (<my-button …>) to the new one (the root element in the template, <li …> ). Transclude moves the content of the old element to the specified (ng-transclude) element. I’m not sure if there’s a simple way to change which element in the template that will receive the migrated attributes.

To achieve what you want you could probably do some dom manipulation in a custom compile function in the my-button directive. However, I think it’d be a better idea to create a new isolate scope in the my-button directive:

<my-buttons>
  <my-button click-fn="doOneThing()">abc</my-button>
  <my-button click-fn="doAnotherThing()">def</my-button>
</my-buttons>

(notice I’ve changed ng-click to click-fn)

module.directive('myButtons', function () {
  return {
    restrict:'E',
    transclude:true,
    replace:true,
    template:'<ul class="u" ng-transclude></ul>'
  }
});

module.directive('myButton', function () {
  return {
    scope:{clickFn:'&'},
    restrict:'E',
    transclude:true,
    replace:true,
    template:'<li class="l"><button ng-click="clickFn()" ng-transclude></button></li>'
  }
});

I’ve also made a working version of your fiddle.

To understand how the isolate scope works (scope:{clickFn:’&’}) I recommend you read the angular guide on directives.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply