AngularJS: 'Template for directive must have exactly one root element' when using 'th' tag in directive template

58,275

Solution 1

I expect that the <th> is getting melted away at some intermediate point when it is evaluated outside the context of a <tr> (put that template into some random part of your webpage to see the <th> disappear).

In your position, I would use a <div> in the template, change sort-by-directive to an 'A' type directive, and use a <th sort-by-directive>...</th> as before, without replace: true.

Solution 2

I've encountered oddities like that with directive and table elements. See this issue for example. Try wrapping your template with div tag or use replace:false.

Solution 3

This isn't your case, but I had this very same issue because my code had html comments before and after the template markup, like so:

<!-- Foo Widget -->
<div class="foo-widget">[...]</div>
<!-- end:: Foo Widget -->

I got rid of the comments and voilá - problem solved.

Solution 4

This error can be also caused by the fact that you need to have a wrapping element for all your tags in the directive's template. Your directive's template can't be only:

<nav></nav>
<div></div>

It must be:

<div>
 <nav></nav>
 <div></div>
</div>

Solution 5

I got this error when I used the template property of the directive definition when I should've been using templateUrl if that helps anyone.

Share:
58,275
Andrei
Author by

Andrei

Updated on March 23, 2021

Comments

  • Andrei
    Andrei over 3 years

    I'm trying to implement custom sortBy directive in order to make columns in html table sortable.

    HTML:

    <thead>
       <tr>
        <sort-by-directive
          ng-repeat="header in headers"
          onsort="onSort"
          sortdir="filterCriteria.sortDir"
          sortedby="filterCriteria.sortedBy"
          sortvalue="{{ header.value }}">{{ header.title }}
        </sort-by-directive>
      </tr>
    </thead>
    

    JS:

    angular.module('mainApp.directives').directive('sortByDirective', function () {
    
            return {
                templateUrl: 'SortHeaderTemplate',
                restrict: 'E',
                transclude: true,
                replace: true,
                scope: {
                    sortdir: '=',
                    sortedby: '=',
                    sortvalue: '@',
                    onsort: '='
                },
                link: function (scope, element, attrs) {
                    scope.sort = function () {
                        if (scope.sortedby == scope.sortvalue)
                            scope.sortdir = scope.sortdir == 'asc' ? 'desc' : 'asc';
                        else {
                            scope.sortedby = scope.sortvalue;
                            scope.sortdir = 'asc';
                        }
                        scope.onsort(scope.sortedby, scope.sortdir);
                    }
                }
            };
        });
    

    Directive Template:

    <script id="SortHeaderTemplate" type="text/ng-template">
    <th ng-click="sort(sortvalue)">
      <span ng-transclude=""></span>
      <span ng-show="sortedby == sortvalue">
        <i ng-class="{true: 'sorting_asc', false: 'sorting_desc'}[sortdir == 'asc']"></i>
      </span>
      <span ng-show="sortedby != sortvalue">
        <i ng-class="{true: 'sorting', false: 'sorting'}[sortdir == 'asc']"></i>
      </span>
    </th>
    </script>
    

    So when I use th as root tag of directive template I retrieve an error:

    Error: [$compile:tplrt] Template for directive 'sortByDirective' must have exactly one root element. SortHeaderTemplate
    

    but when I change th to a or span tags everything works fine.

    What am I doing wrong?