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

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


      ng-repeat="header in headers"
      sortvalue="{{ header.value }}">{{ header.title }}


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 ng-show="sortedby != sortvalue">
    <i ng-class="{true: 'sorting', false: 'sorting'}[sortdir == 'asc']"></i>

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?


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.