I am trying to nest ng-repeat but looks like I am not doing it correctly.
I need all the lineItem in the json to be displayed.
Since, json value I am trying to display is a 3rd level array, I tried nested
ng-repeat but does not work.
<table border="1" width="100%">
<tr>
<th>Id</th>
<th>materialNumber</th>
<th>quantity</th>
</tr>
<tbody ng-repeat="subConfig in values.subConfigs.subConfig">
<tr ng-repeat="lineItem in subConfig.lineItems.lineItem">
<td>{{lineItem.lineItemId}}</td>
<td>{{lineItem.materialNumber}}</td>
<td>{{lineItem.quantity}}</td>
</tr>
</tbody>
</table>
here is jsfiddle I tried:
Your json was not in correct format values should not be array also you need to change ng-repeat="s in values.subConfigs.subConfig"> to ng-repeat="s in values.configBOM.subConfigs.subConfig">
Something like
<tbody ng-repeat="s in values.configBOM.subConfigs.subConfig">
<tr ng-repeat="lineItem in s.lineItems.lineItem">
<td>{{lineItem.lineItemId}}</td>
<td>{{lineItem.materialNumber}}</td>
<td>{{lineItem.quantity}}</td>
</tr>
</tbody>
Here is working fiddle
Its a very minor mistake :
Please change your json to an object instead of an array , the format you have given is wrong .
So it should be :
$scope.values={} //whatever you want to write inside
instead of:
$scope.value=[];
Secondly while you are doing ng-repeat you have to change this line :
<tbody ng-repeat="subConfig in values.subConfigs.subConfig">
to :
<tbody ng-repeat="subConfig in values.configBOM.subConfigs.subConfig">
I got a problem on getting data from ng-repeat.
<tr ng-repeat="x in records">
<td>{{x.id}}</td>
<td>{{x.title}}</td>
<td>{{x.year}}</td>
<td>{{x.note}}</td>
</tr>
The {{x.id}} is the tt0013158 I want to get from the JSON.
Here is the JSON:
{"result":{
"tt0013158":{
"note":"",
"title":"The Frozen North",
"year":"1922"
},
"tt1605783":{
"note":"",
"title":"Midnight in Paris",
"year":"2011"
}
}}
I can get the note, title and year correctly. But how can I get the id (such as tt0013158) from the JSON?
There is no notion in javascript (or angular) that tt0013158 in the sample above would be the object's id. It's just a key in a map.
I believe this should do the trick:
<tr ng-repeat="(key, x) in records.result">
<td>{{key}}</td>
<td>{{x.title}}</td>
<td>{{x.year}}</td>
<td>{{x.note}}</td>
</tr>
you need to use (key,value) syntax :
<tr ng-repeat="(key, value) in data in records.result">
<td>{{key}}</td>
<td>{{value.id}}</td>
<td>{{value.title}}</td>
<td>{{value.year}}</td>
<td>{{value.note}}</td>
</tr>
You can use following approach to iterate through ng-repeat
<tr ng-repeat="item in records">
<td>{{item.id}}</td>
<td>{{item.note}}</td>
<td>{{item.title}}</td>
<td>{{item.year}}</td>
</tr>
I have a JSON object which is data for a chart. This has 2 properties labels and data. Both are arrays. Along with the chart I wish to display a table as well. I am not able to figure out how to use the ng-repeat directive here.
JSON Object
$scope.chartdata={
labels: ["XYZ", "ABC","DEF"],
data: [4286, 38870, 3955]
};
HTML
<table class="table table-striped">
<thead>
<tr>
<th>Labels</th>
<th>Data </th>
</tr>
</thead>
<tr ng-repeat="cht in chartdata">
<td>{{cht.labels}}</td>
<td>{{cht.data}}</td>
</tr>
</table>
Do I need to change the JSON structure? I do not wish to cos it works for my chart directive and I want to resues the same JSON for displaying the table as well.
You need to use $index, because these are 2 separate properties within the chartdata object
<table class="table table-striped">
<thead>
<tr>
<th>Labels</th>
<th>Data </th>
</tr>
</thead>
<tr ng-repeat="label in chartdata.labels">
<td>{{label}}</td>
<td>{{chartdata.data[$index]}}</td>
</tr>
</table>
That said the object structure you have can probably be revised to be more like the model (unless you are using the object structure for something else that expects it in that format)
$scope.chartdata = [
{
label: "XYZ",
data: 4286
},
...
];
In the latter case too, you could use the above structure and set up a method that transforms the object into the dual array structure that you need for the something else.
I have a nested array list and I would like to get a different table row for every unit.
JSON:
[
{
"id":1,
"units":[
{
"id":1,
"name":"Test 1",
},
{
"id":2,
"name":"Test 2"
}
]
}
]
So Test 1 and Test 2 should be in different table rows.
HTML:
<tr ng-repeat="user in users">
<td><p ng-repeat="unit in user.units">{{unit.name}}</p></td>
</tr>
The above code works but I am getting unit names in the same row.
I tried:
<tr ng-repeat="unit in users.units">
<td>{{unit.name}}</td>
</tr>
But that doesn't work. Any help will be appreciated!
Here's a solution that won't require any changes to your data, though it may not be the cleanest solution. The other option (which I'm not going to write up) is to modify your data and flatten the data down to a single array or object containing all the units.
I'm going to make an assumption that you want your DOM (including the <table>) to ideally look like this:
<table>
<tr>
<td><p>Test 1</p></td>
</tr>
<tr>
<td><p>Test 2</p></td>
</tr>
</table>
The problem here is there isn't anything that represents the "users" part of that first ng-repeat (2 ng-repeats are needed for this data structure). As far as I'm aware it's not possible to start an ng-repeat without also creating a DOM element (at least a temporary one), so we can create some valid html (<caption>s in this case) and have angular exclude it using a one-time binding ng-if.
<div ng-controller="MyCtrl">
<table>
<!-- The ng-if here will remove the <caption> from the end result. It's just a placeholder for the users repeater. -->
<caption ng-repeat-start="user in users" ng-if="::false"></caption>
<tr ng-repeat="unit in user.units">
<td><p>{{unit.name}}</p></td>
</tr>
<caption ng-repeat-end ng-if="::false"></caption>
</table>
</div>
I need to use ng-repeat (in AngularJS) to list all of the elements in an array.
The complication is that each element of the array will transform to either one, two or three rows of a table.
I cannot create valid html, if ng-repeat is used on an element, as no type of repeating element is allowed between <tbody> and <tr>.
For example, if I used ng-repeat on <span>, I would get:
<table>
<tbody>
<span>
<tr>...</tr>
</span>
<span>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
</span>
<span>
<tr>...</tr>
<tr>...</tr>
</span>
</tbody>
</table>
Which is invalid html.
But what I need to be generated is:
<table>
<tbody>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
</tbody>
</table>
where the first row has been generated by the first array element, the next three by the second and the fifth and sixth by the last array element.
How can I use ng-repeat in such a way that the html element to which it is bound 'disappears' during rendering?
Or is there another solution to this?
Clarification: The generated structure should look like below. Each array element can generate between 1-3 rows of the table. The answer should ideally support 0-n rows per array element.
<table>
<tbody>
<!-- array element 0 -->
<tr>
<td>One row item</td>
</tr>
<!-- array element 1 -->
<tr>
<td>Three row item</td>
</tr>
<tr>
<td>Some product details</td>
</tr>
<tr>
<td>Customer ratings</td>
</tr>
<!-- array element 2 -->
<tr>
<td>Two row item</td>
</tr>
<tr>
<td>Full description</td>
</tr>
</tbody>
</table>
As of AngularJS 1.2 there's a directive called ng-repeat-start that does exactly what you ask for. See my answer in this question for a description of how to use it.
Update: If you are using Angular 1.2+, use ng-repeat-start. See #jmagnusson's answer.
Otherwise, how about putting the ng-repeat on tbody? (AFAIK, it is okay to have multiple <tbody>s in a single table.)
<tbody ng-repeat="row in array">
<tr ng-repeat="item in row">
<td>{{item}}</td>
</tr>
</tbody>
If you use ng > 1.2, here is an example of using ng-repeat-start/end without generating unnecessary tags:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
angular.module('mApp', []);
</script>
</head>
<body ng-app="mApp">
<table border="1" width="100%">
<tr ng-if="0" ng-repeat-start="elem in [{k: 'A', v: ['a1','a2']}, {k: 'B', v: ['b1']}, {k: 'C', v: ['c1','c2','c3']}]"></tr>
<tr>
<td rowspan="{{elem.v.length}}">{{elem.k}}</td>
<td>{{elem.v[0]}}</td>
</tr>
<tr ng-repeat="v in elem.v" ng-if="!$first">
<td>{{v}}</td>
</tr>
<tr ng-if="0" ng-repeat-end></tr>
</table>
</body>
</html>
The important point: for tags used for ng-repeat-start and ng-repeat-end set ng-if="0", to let not be inserted in the page. In this way the inner content will be handled exactly as it is in knockoutjs (using commands in <!--...-->), and there will be no garbage.
You might want to flatten the data within your controller:
function MyCtrl ($scope) {
$scope.myData = [[1,2,3], [4,5,6], [7,8,9]];
$scope.flattened = function () {
var flat = [];
$scope.myData.forEach(function (item) {
flat.concat(item);
}
return flat;
}
}
And then in the HTML:
<table>
<tbody>
<tr ng-repeat="item in flattened()"><td>{{item}}</td></tr>
</tbody>
</table>
The above is correct but for a more general answer it is not enough. I needed to nest ng-repeat, but stay on the same html level, meaning write the elements in the same parent.
The tags array contain tag(s) that also have a tags array.
It is actually a tree.
[{ name:'name1', tags: [
{ name: 'name1_1', tags: []},
{ name: 'name1_2', tags: []}
]},
{ name:'name2', tags: [
{ name: 'name2_1', tags: []},
{ name: 'name2_2', tags: []}
]}
]
So here is what I eventually did.
<div ng-repeat-start="tag1 in tags" ng-if="false"></div>
{{tag1}},
<div ng-repeat-start="tag2 in tag1.tags" ng-if="false"></div>
{{tag2}},
<div ng-repeat-end ng-if="false"></div>
<div ng-repeat-end ng-if="false"></div>
Note the ng-if="false" that hides the start and end divs.
It should print
name1,name1_1,name1_2,name2,name2_1,name2_2,
I would like to just comment, but my reputation is still lacking. So i'm adding another solution which solves the problem as well. I would really like to refute the statement made by #bmoeskau that solving this problem requires a 'hacky at best' solution, and since this came up recently in a discussion even though this post is 2 years old, i'd like to add my own two cents:
As #btford has pointed out, you seem to be trying to turn a recursive structure into a list, so you should flatten that structure into a list first. His solution does that, but there is an opinion that calling the function inside the template is inelegant. if that is true (honestly, i dont know) wouldnt that just require executing the function in the controller rather than the directive?
either way, your html requires a list, so the scope that renders it should have that list to work with. you simply have to flatten the structure inside your controller. once you have a $scope.rows array, you can generate the table with a single, simple ng-repeat. No hacking, no inelegance, simply the way it was designed to work.
Angulars directives aren't lacking functionality. They simply force you to write valid html. A colleague of mine had a similar issue, citing #bmoeskau in support of criticism over angulars templating/rendering features. When looking at the exact problem, it turned out he simply wanted to generate an open-tag, then a close tag somewhere else, etc.. just like in the good old days when we would concat our html from strings.. right? no.
as for flattening the structure into a list, here's another solution:
// assume the following structure
var structure = [
{
name: 'item1', subitems: [
{
name: 'item2', subitems: [
],
}
],
}
];
var flattened = structure.reduce((function(prop,resultprop){
var f = function(p,c,i,a){
p.push(c[resultprop]);
if (c[prop] && c[prop].length > 0 )
p = c[prop].reduce(f,p);
return p;
}
return f;
})('subitems','name'),[]);
// flattened now is a list: ['item1', 'item2']
this will work for any tree-like structure that has sub items. If you want the whole item instead of a property, you can shorten the flattening function even more.
hope that helps.
for a solution that really works
html
<remove ng-repeat-start="itemGroup in Groups" ></remove>
html stuff in here including inner repeating loops if you want
<remove ng-repeat-end></remove>
add an angular.js directive
//remove directive
(function(){
var remove = function(){
return {
restrict: "E",
replace: true,
link: function(scope, element, attrs, controller){
element.replaceWith('<!--removed element-->');
}
};
};
var module = angular.module("app" );
module.directive('remove', [remove]);
}());
for a brief explanation,
ng-repeat binds itself to the <remove> element and loops as it should, and because we have used ng-repeat-start / ng-repeat-end it loops a block of html not just an element.
then the custom remove directive places the <remove> start and finish elements with <!--removed element-->
<table>
<tbody>
<tr><td>{{data[0].foo}}</td></tr>
<tr ng-repeat="d in data[1]"><td>{{d.bar}}</td></tr>
<tr ng-repeat="d in data[2]"><td>{{d.lol}}</td></tr>
</tbody>
</table>
I think that this is valid :)