5

I've got a carousel directive which includes some chunking to map the passed in array of items into an array of arrays of elements structure which then generates markup similar to the pseudo code below:

<array of arrays>

  <array of items>
    <item>
    <item>
  </array of items>

  <array of items>
    <item>
    <item>
  </array of items>

</array of arrays>

The angular template for this looks like this:

<div class="carousel__content" ng-style="carousel.containerWidth">
  <ul class="carousel__chunk" ng-repeat="chunk in carousel.chunks" ng-style="carousel.chunkWidth">
    <li class="carousel__item" ng-repeat="item in chunk" ng-style="carousel.itemWidth">
      <div class="carousel__item-content">

        [element should be transcluded into this spot.]

      </div>
    </li>
  </ul>
</div>

Given my view code:

<!-- The <a> tag should appear inside the 'carousel.html' template's ng-repeat list. -->
<carousel items="items" config="config">
  <a href="#">{{ item.name }}</a>
</carousel>

I would want the transcluded element to bind to the item object of the deepest ng-repeat

A full Plunker with a reduced test case is available here: http://plnkr.co/edit/kYItcXV0k9KvnpiYx1iG

The problem is that I cannot put a ng-transclude attribute inside the ng-repeat and that (as the carousel.js directive file in the Plunkr shows) I can't seem to inject the to-be-transcluded markup manually into that spot either using the transclude() function in the compile step of the directive.

Any ideas would be much appreciated.

John Slegers
  • 45,213
  • 22
  • 199
  • 169
Jannis
  • 17,025
  • 18
  • 62
  • 75

1 Answers1

6

Set a variable with a reference to the transclude function in the link function of your existing directive:

 post: function($scope, $element, attrs) {
     $scope.transclude = transclude; 
     ...

Then, create a new directive to use in place of ng-transclude on the element you wish for the transcluded content to appear inside of:

.directive('innerTransclude', function(){
  return function(scope, elem, attrs){
    scope.transclude(scope, function(clone){
      elem.append(clone);
    });
  }
})

This directive simply appends the clone to the element, while avoiding the issue with trying to use transclusion inside of an element which uses transclusion itself. Don't forget to add it to your template:

<div class="carousel__item-content" inner-transclude></div>

Demo

Marc Kline
  • 9,399
  • 1
  • 33
  • 36
  • Perfect, works exactly as I need it to, thanks. One question though: Would you be able to explain why this works as opposed to using the transclude function in the pre compile step? I still can't quite figure that out. – Jannis Jun 05 '14 at 04:04
  • 4
    It's actually [**a bug**](https://github.com/angular/angular.js/pull/7387), which has been fixed in the latest beta release of 1.3. This solution is a workaround for older releases. Basically, you can't use `ng-transclude` inside of another directive using transclusion within your template. The workaround works because it stores the transclude function inside of a variable which is not affected by the nested transclusion-using directives, and it does the same thing as ng-transclude without actually using it. – Marc Kline Jun 05 '14 at 04:28
  • 2
    Here's [another fork](http://plnkr.co/edit/J0kdawuNZW1sA4HQccQ9?p=preview) of your Plunker which uses the latest beta. You'll notice that it works as you would have initially expected. – Marc Kline Jun 05 '14 at 04:29
  • Awesome, thanks so much for explaining that and good to know that I was at least on the right track :) – Jannis Jun 05 '14 at 05:01