1

I´m trying to do a parent directive with 2 child directives. The main reason to do this is to force that all the tables in the application use the same "template".

My problem here is that i need to copy the Dom inside the directives. So my main directive is the table directive, and the frist child is the thead and the second child is the tbody.

This is the markup

<div my-table >
  <my-thead>
      <tr>
          <th ng-click="alert('Id')"> Id </th>
          <th ng-click="alert('Name')"> Name </th>
      </tr>
  </my-thead>
  <my-tbody>
      <tr ng-repeat ="item in List">
          <td>{{item.Id}}</td>
          <td>{{item.Name}}</td>
      </tr>
  </my-tbody>
</div>

The directive my-table wil generate all the code necessary for the struct of a table while the dir my-thead and my-tbody will create the tags thead and tbody and give them to do parent div. My problem remains on copying the inner markup of my-thead and my-tbody since they are tr, any suggestion?

Next i present to you the most recent code i get so far with a plunker

myTable

angular
.module('App')
.directive('myTable', ['$compile',
  function($compile){
    var tableSettings = {};
      return {
        restriction :'AE',
        scope : {},
        replace : false,
        link : function(scope,element){
        var divContainerFluid = angular.element('<div>');
        var divRowTable = angular.element('<div>');
        var divColTable = angular.element('<div>');

        divContainerFluid.addClass('container-fluid');
        divRowTable.addClass('Row');
        divColTable.addClass('col-lg-12');

        var divTable = angular.element('<div>');
        var table = angular.element('<table>');

        table.addClass('table');
        table.addClass('table-striped');

        //add from thead
        table.append(tableSettings.tHead);
        //add from tbody
        table.append(tableSettings.tBody);

        divTable.append(table);

        divColTable.append(divTable);

        divRowTable.append(divColTable);

        divContainerFluid.append(divRowTable);

        $compile(divContainerFluid)(scope);

        element.append(divContainerFluid);
      },
      controller: function () {

        this.receiveTHead = function (tHead) {
          tableSettings.tHead = tHead;
        }

        this.receiveTBody = function (tBody) {
          tableSettings.tBody = tBody;
        }
      }
    }
 }]);

myHead

angular
.module('App')
.directive('myThead', [
  function() {
    return {
      restriction: "E",
      scope: true,
      require: "^myTable",
      replace: true,
      transclude: true,
      template:'<thead class="form-class" ng-cloak>' +
               '</thead>',
      link: function(scope, element, attrs, myTableCtrl, transclude)
      {
        transclude(function(clone){
          element.append(clone);
        });
        myTableCtrl
          .receiveTHead(element);
      }
    };
  }
]);

myBody

angular
.module('App')
.directive('myThead', [
  function() {
    return {
      restriction: "E",
      scope: true,
      require: "^myTable",
      replace: true,
      transclude: true,
      template:'<thead class="form-class" ng-cloak>' +
               '</thead>',
      link: function(scope, element, attrs, myTableCtrl, transclude)
      {
        transclude(function(clone){
          element.append(clone);
        });
        myTableCtrl
          .receiveTHead(element);
      }
    };
  }
]);

Plunker

Thanks, for the help.

UPDATE

missing listCtrl

angular.module('App',[])
.controller('listCtrl',['$scope','$timeout',
  function($scope,$timeout){
    $timeout(function () {
      $scope.List = [ {Id : '1', Name : 'Name1'}, {Id : '2', Name :'Name2'}];
    }, 1500);
  }
]);

UPDATE

I recreated the problem from a different perspective. But the body doesn't get populated with the info in the scope.List, any ideas?

<div my-table>
<table>
    <thead>
        <tr>
            <th ng-click="resort('Id')"> Id </th>
            <th ng-click="resort('Name')"> Name </th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="user in List">
            <td>{{user.Id}}</td>
            <td>{{user.Name}}</td>
        </tr>
    </tbody>
</table>
</div>

myTable

angular
.module('App')
.directive('myTable', [
    '$compile',
    function($compile) {
        return {
            restriction: 'AE',
            //scope: {
            //    plEmptytext: '@'
            //},
            transclude: true,
            template:
                '<div class="container-fluid">' +
                    '<div class="row">' +
                    '<div class="col-lg-12">' +
                    '<div ng-transclude></div>' +
                    '</div>' +
                    '</div>' +
                    '</div>',
            link: function(scope, element) {

                var table = element.find('table');

                table.addClass('table');
                table.addClass('table-striped');

                var tHead = element.find('thead');

                var tBody = element.find('tbody');
                tBody.attr('ng-controller', 'listCtrl');

                $compile(tHead)(scope);
                $compile(tBody)(scope);
                $compile(table)(scope);

            }
        };
    }
]);

the listCtrl is the same

Plunker 2

SOLUTION:

For the ones who might come here my solution has to add to the directive tBody return{ controller: 'listCtrl' ...

Jose Rocha
  • 1,115
  • 7
  • 20
  • Setting asside how you have approached this problem I want to clarify what you are trying to accomplish: *You want to create a table directive that takes an array and builds out a table 1. for ease of use and 2. so all tables in your project look the same?* – Sean Larkin Jan 20 '16 at 19:23
  • @SeanLarkin Yes that is what i want. I create a dummy in a listCtrl i´m going to update the question with that listCtrl. This is a snippet that i take from the code that i´m developing, right now that List will be the result of injecting an factory into this directory so i can just give the name of the factory and use it to populate the table with an ajax request from an API, and yes this way all the tables will look the same in all the project and i will just have to give the name of the columns i want to present and the factory where to fetch the data. But you described correctly. – Jose Rocha Jan 20 '16 at 19:33
  • use `ng-Transclude` which will take care of innerHTML. http://stackoverflow.com/questions/24725399/what-is-ng-transclude – agm92 Jan 20 '16 at 21:04

3 Answers3

1

A different approach that might still provide your desired "one template for all" goal would be writing an html template file, and referencing it whenever a table is used by declaring an ng-include. Similar to the old server-side includes, it gives a way to inject a block of html anywhere, anytime, without all the overhead of a directive.

We've actually stepped back from making so many directives and started using includes more when there isn't any work to be done in the directive.

Angular documentation on ngInclude

Daniel Nalbach
  • 1,033
  • 11
  • 17
  • It is not a bad approach but, I don't want to put html templates to do this. I need that the angular takes care off all the creation of the struct. – Jose Rocha Jan 20 '16 at 20:07
  • @JoseRocha You can still use normal Angular directives in the HTML of an ng-include like ng-repeat and all of that. It is not limited to HTML, it's just a view section that you inject and unlike a directive, does not have it's own scope, but uses the scope of the controller view that it was included from. Recommend taking a closer look. – Daniel Nalbach Jan 20 '16 at 20:27
0

Sounds like you need a "Multi-slot Transclusion".

This is available in angular versions > 1.5 https://docs.angularjs.org/api/ng/directive/ngTransclude

0

For the ones who might come here my solution has to add to the directive tBody

return{ controller: 'listCtrl' ...

For me it was the solution I was looking for.

Jose Rocha
  • 1,115
  • 7
  • 20