Search code examples
javascriptangularjsangularjs-ng-repeatangularjs-ng-transclude

angularjs transclude and ng-repeat: doing it right


I have the following code that produces not the expected result:

<div class="outer">1<div>content</div></div>
<div class="outer">2<div>content</div></div>
<div class="outer">3<div>content</div></div>
<div class="outer">4<div>content</div></div>

Instead the result is:

<div class="outer">1</div>
<div class="outer">2</div>
<div class="outer">3</div>
<div class="outer">4<div>content</div></div>

It seems that the ng-repeat gets done first and for the last item it handles the transclude. I know that ng-repeat create the nodes during the compile phase, but I thought in the link phase the link function is called for each node and adds the embedded content.

Can somebody explain what is happening here and how to do it correct?

<!DOCTYPE html>
<html ng-app="Transclude">
<head lang="de">
  <meta charset="UTF-8">
  <title>Transclude</title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
</head>
<body>

  <outer ng-repeat="counter in [1,2,3,4]" value="counter">
    <div>content</div>
  </outer>

<script>
  angular.module('Transclude', [])

    .directive('outer', function () {
      return {
        restrict: 'E',
        scope: {
          value: '='
        },
        replace: true,
        transclude: true,
        template: '<div class="outer">{{value}}</div>',

        link: function(scope, element, attributes, controller, transclude) {
          var transcludedContent = transclude();
          element.append( transcludedContent );
        }
      };
    })

</script>

</body>
</html>

Solution

  • transclude() by itself just links the content of the directive to the proper scope and returns it. What you want to do is actually clone the content (make copy of it), before transclude links it. As you code stands right now, your transcluded content is just getting moved from one instance to another - ending up on the last one because, well, it's the last one.

    You can do this with the cloneAttachFn. You pass it in to transclude.

        link: function(scope, element, attributes, controller, transclude) {
          transclude(scope, function(clone) {
              element.append( clone );
          });
    
        }