Significance of ng-repeat-start vs ng-repeat - angularjs

I am trying to understand the significance of ng-repeat-start over ng-repeat. The angular documentation provides the following example for ng-repeat-start
<header ng-repeat-start="item in items">
Header {{ item }}
</header>
<div class="body">
Body {{ item }}
</div>
<footer ng-repeat-end>
Footer {{ item }}
</footer>
But the same can be achived using ng-repeat,
<div ng-repeat="item in items">
<header>
Header {{ item }}
</header>
<div class="body">
Body {{ item }}
</div>
<footer>
Footer {{ item }}
</footer>
</div>
Can someone explain the significance of ng-repeat-start.? Thanks.

I thought I'd add my answer, as no one touched on a very important reason for having these directives available. ng-repeat will not work correctly in certain scenarios when using html tables. Using ng-repeat-start is the only way to accomplish certain things.
Imagine you want to display your data like this using html tables:
And this is your data set:
groups = [
{
name: "Group 1",
customers: [
{id: 123, name: "Foo Inc.", state: "NJ"},
{id: 234, name: "Bar Co.", state: "AZ"}
]
},
{
name: "Group 2",
customers: [
{id: 345, name: "Baz LLC", state: "CA"}
]
}
];
Using ng-repeat-start and ng-repeat-end you can do this:
<table>
<tr>
<th>ID</th>
<th>Customer</th>
<th>State</th>
</tr>
<tr ng-repeat-start="group in groups">
<td colspan="3" style="text-align:center">{{group.name}}<td>
</tr>
<tr ng-repeat-end ng-repeat="customer in group.customers">
<td>{{customer.id}}</td>
<td>{{customer.name}}</td>
<td>{{customer.state}}</td>
</tr>
</table>
Notice the ng-repeat-end followed by another regular ng-repeat. This ends the matching ng-repeat-start but initiates another repeat on the customers array, since we are still in the original ng-repeat-start scope when calling ng-repeat-end, we still have access to the group object.
Keep in mind, this is a very trivial example, but as the table structure becomes more complicated, the only way to accomplish things like this is to use ng-repeat-start and ng-repeat-end

The significance of these two directives is similar: they repeat HTML-tags. The difference is only that with help of ng-repeat-start you could repeat few tags starting from tag with ng-repeat-start and finishing by ng-repeat-end.
For example you have next code:
<div>
Item # {{item}}
</div>
<div>Whether you repeat me?</div>
So now we can add 2 directives for these code.
With ng-repeat:
<div ng-repeat="item in items">
Item # {{item}}
</div>
<div>
This code will not be repeated
</div>
With ng-repeat-start and ng-repeat-end:
<div ng-repeat-start="item in items">
Item # {{item}}
</div>
<div ng-repeat-end="">This code will be repeated</div>
So now you can see that in the first case just div with ng-repeat directive repeats, but in the second case both your divs will be repeated.
You can see Demo and play with it:
Demo: http://plnkr.co/edit/R778lWTABVF3Hy16CAca

The ng-repeat-start directive works the same as ng-repeat, but will repeat all the HTML code (including the tag it’s defined on) up to and including the ending HTML tag where ng-repeat-end is placed

Related

Showing JSON-data in AngularJS

This is how my JSON-data looks like:
var data = [
"val1":[
{"DATE":a1, "HEADER":b1, "MESSAGES":c1},
{"DATE":a2, "HEADER":b2, "MESSAGES":c2},
{"DATE":a6, "HEADER":b6, "MESSAGES":c6},
],
"val2":[
{"DATE":a5, "HEADER":b5, "MESSAGES":c5},
{"DATE":a8, "HEADER":b8, "MESSAGES":c8},
],
"val3":[
{"DATE":a3, "HEADER":b3, "MESSAGES":c3},
{"DATE":a4, "HEADER":b4, "MESSAGES":c4},
{"DATE":a7, "HEADER":b7, "MESSAGES":c7},
],
];
Now I want to use this data in html. Therefore I use AngularJS (although this is an old framework). The data is correctly sended to the client side but now I have to display it correctly. I already have this:
<ul>
<li class="message" ng-repeat="messages in data">
{{ messages }}
</li>
</ul>
Now it's showing all the data (which isn't my goal). It should be like this:
Each value (like "val1" and "val2" etc.) should be displayed. Therefore i use ng-repeat.
But now I also want to show the data inside. I thought that I could make another ng-repeat to display all the values of the arrays inside "val1" etc.
But how could I do this and have access to those values?
This HTML should display every item inside val1 array, val2 array and so on:
<ul>
<li class="message" ng-repeat="messages in data">
<span ng-repeat="item in messages">
{{ item }}
<span>
</li>
</ul>
The line <li class="message" ng-repeat="messages in data"> will repeat 1'st level arrays inside data array, and
<span ng-repeat="item in messages">
{{ item }}
<span>
will iterate and display every 2'nd level item, such as {"DATE":a1, "HEADER":b1, "MESSAGES":c1} and so on.

Add in Hash and update DOM element dynamically using ng-repeat

I have hash of users as the following:
{"A1": {name: "Demo-1", status: "active"}, "A2": {name: "Demo-2", status: "deactive"}};
And I have two hashes of active/deactive users, and I make the table of each of them as the following:
<tr ng-repeat="(id, user) in deactiveUsers">
<td>
{{ user.name }}
</td>
<td>
<button type="button" ng-click="change(id)">Active</button>
</td>
</tr>
And If I click on Active button, I remove the object from deactiveUsers, and add it in activeUsers, the change function as the following:
$scope.change = function(id){
if($scope.users[id].status == "active"){
delete $scope.activeUsers[id];
$scope.users[id].status = "deactive";
$scope.deactiveUsers[id] = $scope.users[id];
}else{
delete $scope.deactiveUsers[id];
$scope.users[id].status = "active";
$scope.activeUsers[id] = $scope.users[id];
}
}
This my demo, it's working well if I tried it on plnkr. But on my localhost the deleting only is working, when I add on activeUsers the DOM element doesn't update with the new record.
I can't find out the issue.
How can I add in Hash to update the DOM element dynamically ?
why it's working on plnkr, but the same code doesn't work on localhost ?
You have multiple body tags in your DOM. I think the first (after the first table element) cause the others angular directives are outside the app.
Replace the body in your table elements by tbody elements.
Edit By the question's owner
My Error was I developed the tables as the following:
<div class="panel panel-primary" ng-controller="ExampleCtrl">
<table></table>
</div>
<div class="panel panel-primary" ng-controller="ExampleCtrl">
<table></table>
</div>
So, I called ng-controller twice, I fixed it by develop one div for the both as the following:
<div ng-controller="ExampleCtrl">
<div class="panel panel-primary">
<table></table>
</div>
<div class="panel panel-primary">
<table></table>
</div>
</div>

Hide table header if no results after filter

I have a table containing some data. And i have to filters on this data.
When, after filtering data, there is no results, i want to show a "no results" message, and it works.
But i also want to hide the table completely when there are no results.
Here is a demo :
http://plnkr.co/edit/iSbr9Ij4wsX947JqAsEB?p=preview
I tried :
<table ng-if="results.length > 0">
But that gave me a js error
Is there a way to hide the table if there are no results to show ?
Thanks a lot
this does not feel right, but it works :)
<tr ng-repeat="x in accueils | filter:{annee:annee.id} | filter:myFilter as results" ng-if="$first">
<th>column1</th>
<th>column2</th>
</tr>
EDIT This feels nicer .
<header ng-repeat-start="item in items">
Header {{ item }}
</header>
<div class="body">
Body {{ item }}
</div>
<footer ng-repeat-end>
Footer {{ item }}
</footer>

How do I do nested interpolation in Angular JS

I have an ng-repeat that requires nested interpolation to be evaluated. Here is an example:
<div ng-repeat="i in ['1', '2', '3']">
should evaluate to {{ i }}: {{ (i>0) && ( {{ i }} ) || false }}
</div>
This throughs a $parse error. The problem I figure is that Angular can't perform nested evaluation operations, or support nested interpolation.
Is there a way around this problem?
I had a similar issue when trying to create a dynamic table component. With the following code I'm able to display any table from the database on the front end without having to define anything thanks to nested interpolation.
You just need to use square brackets for the nested interpolation.
E.G: {{row[[col.column_name]]}}
<table id="dynamicTable" class="table table-hover" cellspacing="0">
<thead>
<tr>
<th *ngFor="let col of Columns">{{col.column_name}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of Table">
<td *ngFor="let col of Columns">{{row[[col.column_name]]}}</td>
</tr>
</tbody>
</table>
Hope this helps! (Working with Angular 7)
It's giving you parse error because you can't nest {{}}..
{{ code }} basically tells angular that whatever is inside {{ }} (code in my case) is JavaScript, and js doesn't understand {{ }} syntax. This is why second {{ i }} is causing a problem.
You might also want to look at ngRepeat docs - there are some special properties, like $index, that you can use.. https://docs.angularjs.org/api/ng/directive/ngRepeat
I can't understand what's this {{ i }}: {{ (i>0) && ( {{ i }} ) || false }} statement is about , also you cannot have nested {{}} in angular js.
Are you trying to make
Working Demo
<div ng-controller="MyCtrl">
<div ng-repeat="i in ['0','1', '2', '3']">
{{ i>0 ? true: false }}
</div>
</div>
The result of what's in the {{}} will be printed - no need to nest:
<div ng-repeat="i in ['0','1', '2', '3']"> {{ i > 0 ? i : false }} </div>

How to use ng-repeat without an html element

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 :)

Resources