AngularJS ng-repeat with custom element inside a table is rendering strangely
Solution 1
<td>
is known to behave strangely in directives like this. Instead, use a directive on the parent <tr>
. Read more about this issue here: https://github.com/angular/angular.js/issues/1459
<table>
<tr ng-repeat="p in people" my-element></tr>
</table>
Here is how you can further improve your directive so that it is more re-usable.
app.directive('myElement', function () {
return {
scope: {
item: '=myElement'
},
restrict: 'EA',
template: '<td>Name: {{item.name}}</td><td>Age: {{item.age}}</td>'
};
});
and pass in the value of item
like so:
<table>
<tr ng-repeat="person in people" my-element="person"></tr>
</table>
Live Demo
Solution 2
Apply the directive to <tr>
like this:
<table class="table table-hover">
<tr my-element blah='p' ng-repeat="p in people"></tr>
</table>
app.directive('myElement', function () {
return {
restrict: 'A',
scope:{
ngModel: '=blah'
},
template: '<td>Name: {{ ngModel.name }}</td><td>Age: {{ ngModel.age }}</td>'
}
});
Solution 3
Use replace: true
in your directive and your <my-element>
will be replaced with the root item in your template, a <td>
, so this will not confuse the HTML.
ravishi
Updated on January 20, 2020Comments
-
ravishi over 4 years
I'm trying to re-use a portion of my HTML view in multiple places. The portion I want to re-use is table cells in an HTML table. The problem is that my custom directive inside a ng-repeat is doing funny things. I have reproduced the problem on jsFiddle. There are two HTML tables in the jsFiddle. The first is ng-repeat with the table cells written in the view and the second is the table cells coming from a directive, my-element. Chrome dev tools report that the rendered HTML looks like this. Note that the custom element appears only once and is outside the table.
Rendered HTML
<div ng-controller="MyCtrl" class="ng-scope"> table1 <table class="table table-hover"> <tbody><!-- ngRepeat: p in people --> <tr ng-repeat="p in people" class="ng-scope"> <td class="ng-binding">Name: Mike</td> <td class="ng-binding">Age: 20</td> </tr> <tr ng-repeat="p in people" class="ng-scope"> <td class="ng-binding">Name: Peter S</td> <td class="ng-binding">Age: 22</td> </tr> </tbody> </table> <br>table2 <my-element class="ng-binding">Name: Age: </my-element> <table class="table table-hover"> <tbody> <!-- ngRepeat: p in people --> <tr ng-repeat="p in people" class="ng-scope"> </tr> <tr ng-repeat="p in people" class="ng-scope"> </tr> </tbody> </table> </div>
Source HTML
<div ng-controller="MyCtrl"> table1 <table class="table table-hover"> <tr ng-repeat="p in people"> <td>Name: {{ p.name }}</td> <td>Age: {{ p.age }}</td> </tr> </table> <br/>table2 <table class="table table-hover"> <tr ng-repeat="p in people"> <my-element></my-element> </tr> </table> </div>
Source JS
var app = angular.module('myApp', []); app.directive('myElement', function () { return { restrict: 'E', template: '<td>Name: {{ p.name }}</td><td>Age: {{ p.age }}</td>' } }); function MyCtrl($scope) { $scope.people = [{ name: 'Mike', age: 20 }, { name: 'Peter S', age: 22 }]; }
Please note the jsFiddle is a trivial example and common sense would lead to just not using directives at all. However, my target code has a much larger template that I want to re-use. I've tried using "ng-include" as well but the result is similar.
-
Yoshi over 10 yearswith the current template this would result in an Template must have exactly one root element. error.
-
ravishi over 10 yearsBoth m59 and @sza answers worked but I appreciated the additional comments about code improvements.
-
boneskull over 10 yearsAh, yes of course. Maybe wrap in a
<tbody>
if that works within the context of a <tr>.. otherwise see m59's answer. -
chander over 10 yearsI'd avoid this - re-using ng-model seems to unnecessarily confuse things with the normal use of ng-model and bindings with forms, etc.
-
zs2020 over 10 years@TheBigC I think I'd better rename it to something else. Thx.
-
m59 over 10 yearsGood idea, but seems to throw that template error no matter what. Not sure how to fix that.
-
zs2020 over 10 yearsWhat is so special about this solution then?
-
m59 over 10 yearsApologies. The naming made your intent unclear. I thought you were doing something else.
-
zs2020 over 10 years@TheBigC This one should not matter at all.
-
ade jones over 9 yearsthis works, but the rendered html loses the internal td tags. e.g. <td my-element="" class="ng-binding">Name: MikeAge: 20</td> with nothing between Mike & Age. Any idea why?
-
gfaceless over 9 yearsThis seems to work, but as @ade jones pointed out, it actually fails. should either follow zsong's answer or use 'replace: true'. Further information about 'replace: true': github.com/angular/angular.js/issues/1459
-
m59 over 9 years@gfaceless ah yeah, that was a mess. I didn't notice what I had done there. I corrected everything. I was new to Angular back then.
-
Chris Hayes over 8 yearsis there a way to access $index in the template?
-
m59 over 8 years@ChrisHayes Sure, pass it in on the scope.
index="$index"
scope: {index: '=',