johngull
johngull

Reputation: 829

Angular ng-transclude scope in repeat

I just started to study AngularJS and tries to implement customized table directive with the multiple slots transclude. And faced situation that scope not transferred to transclude. There is a lot of solutions in other StackOverflow questions, but all of them works only when in directive template ng-repeat appears for top element, but that is not my case. At least i can't adopt all that solutions.

Simplified version. Directive:

<span>
  <div>Some pagination</div>
  <div style="display: inline"><input type="text" placeholder="Search"/></div>
  <div style="display: inline">Some filters</div>

  <table>
    <tbody>
      <tr ng-repeat="line in lines" ng-transclude="row">
      </tr>
    </tbody>
  </table>

  <div>Some pagination again</div>
</span>

Using of directive:

<my-table>
     <row>
          <td>{{line.col1}}</td>
          <td>{{line.col2}}</td>
      </row>
</my-table>

Full example with the script on Plunkr: https://plnkr.co/edit/rg43ZdPMGHLBJCTLOoLC

Any advice very appreciated.

Upvotes: 2

Views: 953

Answers (3)

jbmilgrom
jbmilgrom

Reputation: 23301

The simplest and probably cleanest way to directly reference the $scope object created by the ng-repeat in a transcluded template is through the $parent property:

<my-table>
  <td>{{$parent.line.col1}}</td>
  <td>{{$parent.line.col2}}</td>
</my-table>

The $parent property of the $scope created for a transcluded template points to the $scope of the target template into which such template is ultimately transcluded (in this case, the ng-repeat), even though such transcluded $scope is not a child of the target $scope in the usual sense as a result of the transclusion. See this wonderful blog post for a more complete discussion of this.

Working plunkr: https://plnkr.co/edit/LoqIMiQVZKlTt5epDnZF?p=preview.

Upvotes: 2

Arthur Tsidkilov
Arthur Tsidkilov

Reputation: 5421

i see you don't really need to use attribute

so code look more simple and clean:

 <body ng-controller="tableCtrl">
   <h1>Table test</h1>
   <my-table lines="lines"></my-table>
 </body>

your template:

    <span>
      <div>Some pagination</div>
      <div style="display: inline"><input type="text" placeholder="Search"/></div>
      <div style="display: inline">Some filters</div>

      <table>
        <tbody>
          <tr ng-repeat="line in lines">
               <td>{{line.col1}}</td>
               <td>{{line.col2}}</td>
          </tr>
        </tbody>
      </table>

      <div>Some pagination again</div>
    </span>

and angular directive:

   angular.module('myApp', [])
    .directive("myTable", function() {
      return {
        restrict: 'E',
        transclude: true,
        scope: {
          lines:'=lines',
          api: '@'
        },
        templateUrl: "template.html",
      };
    })
    .controller("tableCtrl", ['$scope', function($scope) {
      $scope.lines = [
        {col1: "testCol1", col2: "testCol2"},
        {col1: "testCol11", col2: "testCol21"}
        ];
    }]);

working example in plunkr: https://plnkr.co/edit/iMxRoD0N3sUXqmViHAQh?p=preview

Upvotes: 0

sielakos
sielakos

Reputation: 2404

You need to use $transclude function manually and create new child scope for each line. Other than that you need to pass lines to directive if you are using isolated scope (and you are using it). Your linking function should look something like this:

link: function($scope, $element, $attrs, controller, $transclude) {
      var tbody = $element.find('tbody');

      $scope.$watch('lines', function (lines) {
        tbody.empty();

        lines.forEach(function (line) {
          var childScope = $scope.$new();
          childScope.line = line;

          $transclude(childScope, function (content) {
            tbody.append('<tr>');
            tbody.append(content);
            tbody.append('</tr>');
          }, null,  'row');
        });
      });
    }

Plunker: https://plnkr.co/edit/MLNZOmoQyMazgIpluMqO?p=preview

But that is bad idea anyway, cos it is hard to create table this way. As you can see child of is not elements. You would have to do a little bit of DOM manipulation to make it work.

Upvotes: 1

Related Questions