问题
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?
回答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
.
回答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
.
回答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.
回答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>
回答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.
回答6:
I know this is old, but there is another solution. I encountered this issue also, and tried all the above solutions with no luck.
turns out, for some weird reason, that this error is thrown also in case there is a typo in the 'templateUrl' - if angular can't find the html file by the given path - you get the same 'must have exactly one root element' error.
so - fixing the templateUrl fixed the error for me.
hope this helps anyone in the future.
回答7:
As stated by others: this is because your browser ignores the TH before it gets placed inside the table. My prefered way to fix this is to change the directive to an attribute directive and add it to a TH in the table.
Directive looks like this:
.directive('sortByDirective', function () {
return {
templateUrl: 'SortHeaderTemplate',
restrict: 'A',
transclude: true,
replace: false,
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);
}
}
};
});
Setting it on your page looks like this:
<th sort-by-directive
ng-repeat="header in headers"
onsort="onSort"
sortdir="filterCriteria.sortDir"
sortedby="filterCriteria.sortedBy"
sortvalue="{{ header.value }}">{{ header.title }}
</th>
回答8:
I have encountered the issue couple of times and most of the times it could be that you are not wrapping your elements under one element like
<div>
<div... </div>
</div>
But there was one occasion where you get this error when the template path is not correct. So please check if you are referring to the template correctly.
回答9:
i encounter the following error:
Error: [$compile:tplrt] http://errors.angularjs.org/1.2.6/$compile/tplrt?p0=stockWidget&p1=stock.html.
I get around by removing commement at very top of template file.
replace is deprecated with angularjs 1.3 forward, the next release will remove it completely, it's better not to use replace key.
回答10:
Which version of angular are you using ?
There was a bug for something looking like your problem that was fixed in 1.2.13 1.3 Beta 1 commit link
https://github.com/angular/angular.js/issues/1459
回答11:
I know it's very old answer and question but I encountered this error and fix it by put my comment inside a div tag.
before:
<!--You commented code-->
<div>
</div>
after:
<div>
<!--You commented code-->
</div>
来源:https://stackoverflow.com/questions/22373647/angularjs-template-for-directive-must-have-exactly-one-root-element-when-usin