Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ng-transclude doesn't seem to work well in a table

Tags:

angularjs

I created a simple directive that is used to display some text in a <td> when there is no table data (i.e. "No results found" ) takes up the entire row of the table. Before I just had static text in the <td> but now I want to be able to put any DOM into it. I tried adding ng-transclude to my directive but now it is rendering the element in a strange way.

Here is my directive:

app.directive('skNoResult', ['$rootScope', function () {
    return {
        restrict: 'A',
        replace: true,
        transclude: true,
        template: '<tr ng-if="!hasResult"><td class="left" colspan="{{ colSpan }}"><ng-transclude></ng-transclude></td></tr>',
        link: function (scope, elem, attrs, ctrl) {
            var span = angular.element(elem).parents('tbody').siblings('thead').find('tr').children().length;

            scope.colSpan = span;

            scope.$watch(attrs.skNoResult, function (list) {
                if (list.length) {
                    scope.hasResult = true;
                } else {
                    scope.hasResult = false;
                }
            });
        }
    };
}]);

It basically just keeps track of the data set (array) and checks the length to see whether or not there is any data or not. If there is then we display this row with ngIf.

My html just looks like this

<tr sk-no-result="model.dataSet">Here is my text I want to transclude into my directive</tr>

The problem is that the transcluded text is being inserted into the DOM as just a textNode and is appearing above the <table> rather than inside of it. Any idea why this is happening?

like image 488
Matt Hintzke Avatar asked Sep 25 '14 22:09

Matt Hintzke


1 Answers

I believe the reason you are seeing this is not because of Angular but rather the browser seeing that it is invalid html inside a <tr> as it is expecting <td> and as a result it moves this content to above the table BEFORE Angular even gets a chance to run and do the transclusion. You can easily test this by removing any Angular code and just leave the HTML and you'll notice the outcome is exactly the same.

Here is a workaround that you might be able to use:

<tr ng-if="!model.dataSet.length">
  <td sk-no-result="model.dataSet">Here is my text I want to transclude into my directive</td>
</tr>

And the directive:

app.directive('skNoResult', ['$rootScope', function () {
  return {
    restrict: 'A',
    replace: true,
    transclude: true,
    template: '<td class="left" colspan="{{ colSpan }}"><div ng-transclude></div></td>',
    link: function (scope, elem, attrs) {
        var span = angular.element(elem).parents('tbody').siblings('thead').find('tr').children().length;
        scope.colSpan = span;
    }
  };
}])

Note that the element usage of ngTransclude, i.e. <ng-transclude></ng-transclude> is only available from Angular version 1.3.0-beta.16 and above. If you are using a 1.2 release you need to use the attribute usage as in the example above <div ng-transclude></div>

Here is a working demo.

like image 188
Beyers Avatar answered Nov 04 '22 15:11

Beyers