in Angularjs {{$index}} loop only once insted of loop everytime - angularjs

in Angularjs {{$index}} loop only once insted of loop everytime
<div ng-repeat="(key, value) in reports" class="all_report_admin">
<div class="name">{{key}}</div>
<div ng-repeat="data in value" class="all_report">
<div class="date">{{$index+1}}</div>
<div class="status">{{data.status}}</div>
</div>
</div>
How to restrict that

try ng-repeat="data in value[0]" instead of your ng-repeat

There is no easy combination that will give you such index (also known as a counter). It might look similar to how a one-dimensional array can be indexed with two-dimensional rows and columns, with [column + row * columns] formula (potentially {{$index + $parent.$index * reports[key].length}}). However, your reports can have an arbitrary number of object properties and arrays. So you need a manual counter in your controller.
I came up with this simple thing:
$scope.array = [];
var counter = 0;
for(key in $scope.reports){ // for every object
var temp = []; // row array
for(var j=0; j<$scope.reports[key].length; j++){
counter++;
temp.push(counter); // column element (counter)
}
$scope.array.push(temp);
}
Then it's enough to display it with {{array[$parent.$index][$index]}} in your inner ng-repeat
Here is a demo:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.reports = {
"My reports 1":[
{"status":"status1"},
{"status":"status2"},
{"status":"status3"}
],
"My reports 2":[
{"status":"status1"},
{"status":"status2"},
{"status":"status3"},
{"status":"status4"},
],
"My reports 3":[
{"status":"status1"},
{"status":"status2"},
],
"My reports 4":[
{"status":"status1"},
{"status":"status2"},
{"status":"status3"},
]
};
$scope.array = [];
var counter = 0;
for (key in $scope.reports) { // for every object
var temp = [];
for (var j = 0; j < $scope.reports[key].length; j++) {
counter++;
temp.push(counter);
}
$scope.array.push(temp);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="(key, value) in reports">
<div>{{key}}</div>
<div ng-repeat="data in value">
<strong>INDEX: {{array[$parent.$index][$index]}}</strong>
<div>{{data.status}}</div>
</div>
<hr>
</div>
</div>

Related

Divider into ng-repeat for random number of iterations

I need to create an ng-repeat iteration with divider in it. The problem is that I have to add divider after every two, three or four elements randomly. I started with this code:
<li repeat-start="person in persons track by $index">
<p>{{ person.name }}</p>
</li>
<li ng-repeat-end ng-if="($index + 1) % 2 === 0">
<p>--divider--</p>
</li>
It works great when I specify the exact value of 2 inside ng-if. It shows me result like that.
Person 1
Person 2
--divider--
Person 3
Person 4
--divider--
Person 5
etc...
But how to specify this coefficient dynamically? I added a function to genereate a random number among 2, 3 and 4.
$scope.getNumber = function() {
return Math.floor(Math.random() * (3) + 2);
}
But when I try to change hardcoded value I cannot see the desirable result. None of these solutions works. Neigher with simple function call.
ng-if="($index + 1) % getNumber() === 0"
nor with ng-init variations.
<li repeat-start="person in persons track by $index" ng-init="coeff = getNumber()">
<p>{{ person.name }}</p>
</li>
<li ng-repeat-end ng-if="($index + 1) % coeff === 0">
<p>--divider--</p>
</li>
How to achieve this functionality?
The problem was with Infinite $digest Loop, which was caused by fact, that getNumber function returns random results, so AngularJS can't stabilize itself. To calculate getNumber only once for each ng-repeat iteration, you can use ng-init directive, as you already did (may be your code doesn't work due to typo: repeat-start instead of ng-repeat-start):
angular.module('app', []).controller('ctrl', ['$scope', function($scope) {
$scope.persons = [];
for(var i = 0; i < 20; i++)
$scope.persons.push({name:'Person ' + i});
$scope.getNumber = function() {
return Math.floor(Math.random() * 3 + 2);
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app='app' ng-controller='ctrl'>
<li ng-init='temp = getNumber()' ng-repeat-start="person in persons">
<p>{{person.name}}</p>
</li>
<li ng-repeat-end ng-if="($index + 1) % temp === 0">
<p>--divider--</p>
</li>
</ul>
Another solution is to "cache" results of getNumber function, for example at person entity:
angular.module('app', []).controller('ctrl', ['$scope', function($scope) {
$scope.persons = [];
for(var i = 0; i < 20; i++)
$scope.persons.push({name:'Person ' + i});
$scope.getNumber = function(person) {
return person.temp || (person.temp = Math.floor(Math.random() * 3 + 2));
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app='app' ng-controller='ctrl'>
<li ng-repeat-start="person in persons">
<p>{{person.name}}</p>
</li>
<li ng-repeat-end ng-if="($index + 1) % getNumber(person) === 0">
<p>--divider--</p>
</li>
</ul>
Try this (some refactorings):
<li repeat-start="person in persons track by $index">
<p>{{ person.name }}</p>
<p ng-if="putDividerHere($index)">--divider--</p>
</li>
$scope.putDividerHere(index) {
var randomNumber = Math.floor(Math.random() * (3) + 2);
return ((index + 1) % randomNumber) === 0;
}
A small change needed. Call a function in your ng-if that cals the getNumber function and returns the condition true/false
HTML:
<li ng-repeat-end ng-if="isShowDivider($index+1)">
<p>--divider--</p>
</li>
JS:
$scope.isShowDivider = function (index) {
if (index % $scope.getNumber() !== 0)
return false;
return true;
}
$scope.getNumber = function() {
return Math.floor(Math.random() * (3) + 2);
}
Edit for the iterations reached. Aborting error.
<li ng-repeat="person in persons track by $index">
<p>{{person.name}}</p>
<p ng-if="isShowDivider($index+1)">--divider--</p>
</li>

Serial Number using nested NG-Repeat

I want to add serial number in each question
ng-repeat = "Table in Paper"
Display table
ng-repeat = "question in question"
(1) Display question
(2) Display question
I can use $index in Question. But for each Table, it is started from 1.
E.g.
Table 1
Question 1
Question 2
Table 2
Question 1
Question 2
But I want
Table 1
Question 1
Question 2
Table 2
Question 3
Question 4
I use $parent & $index. But not working.
<div ng-repeat="section in Table">
<div ng-repeat="question in section.question">
<span >{{(Section[$parent.$index-1].question.length + $index + 1 )+
'. ' +
question
.Description}}</span>
</div>
try this.
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', ["$scope",function MyCtrl($scope) {
$scope.lastIndex = 0;
$scope.lastIndexes = [];
$scope.Table ={
"table1":
{
"question":["questionA","questionB","questionC","questionD"]
},
"table2":
{
"question":["questionA","questionB","questionC","questionD"]
},
"table3":
{
"question":["questionC","questionD","questionF"]
},
"table4":
{
"question":["questionC","questionD","questionE"]
}
};
$scope.setLastIndex = function(len){
$scope.lastIndex = $scope.lastIndex + len*1;
$scope.lastIndexes.push($scope.lastIndex);
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<div ng-repeat="(key,value) in Table"
ng-init="setLastIndex(value.question.length)">
{{key}}
<div ng-repeat="question in value.question" >
<span>{{($index+1 +lastIndexes[$parent.$index-1])}} :{{question}}</span>
</div>
</div>
use
<div ng-repeat = "Table in Paper">
<div ng-repeat="q in Table.questions">
{{q}}
</div>
</div>
Here questions is array and present in your json
This may not the perfect solution what I want, But It can be a temporary solution,
Create watch or function in controller to traverse all question
var index=1;
Section.forEach(function (section) {
section.question.forEach(function (question) {
question.Number= index;
index++;
})
})
Then Print this number in ng-repeat
<div ng-repeat="section in Table">
<div ng-repeat="question in section.question">
<span >{{question.Number +'. ' +question.Description}}</span>
</div>
</div>

Angularjs ng-repeat sum the fields

Hi I am new to angularjs I have a problem in calculation the sum of the field my code is like this
<div ng-repeat="(key,item) in expenses_data | groupBy: 'category_name'">
<h4 ng-cloak><i class="fa fa-plus-circle"></i> {{ key }}</h4>
<div class="form-group" ng-repeat="expn_head in item">
<label class="col-sm-3 control-label">{{ expn_head.name }}</label>
<div class="col-sm-2">
<input type="text" class="form-control input-sm" ng-model="expenses.expenditure_head[expn_head.id]">
</div>
</div>
</div>
How do I sum up all the expenses.expenditure_head value entered and put it in the new text field. Is there any function like this:
<input type=text ng-value="{{ sum() }}">
and js
$scope.sum = function() {
var total = 0;
for (var i = 0; i < $scope.expenses.expenditure_head; i++) {
var myValue = $expenses.expenditure_head[i];
total += myValue;
}
return total;
}
json
[
{
"id":23,
"name":"Agency Commission",
"expenditure_category_id":1,
"category_name":"Main"
},
{
"id":22,
"name":"Bonus to Local Staff",
"expenditure_category_id":1,
"category_name":"Main"
},
{
"id":48,
"name":"Advance for Expenses",
"expenditure_category_id":2,
"category_name":"Other Dept's Budget Exp"
},
{
"id":49,
"name":"Agency TDS",
"expenditure_category_id":2,
"category_name":"Other Dept's Budget Exp"
}
]
Can anyone show me the right direction? I have trying this for a day.
The right way is to do so inside the controller. Create a function call it getSum()
$scope.getSum = function(){
var total = 0;
for(var i = 0; i < $scope.someVar; i++){
var myValue = $someVar[i];
sum += myValue;
}
return sum;
};
Then inside your code you do something like
<td>Total: {{ getSum() }}</td>

Add item in array repeatedly for ng-repeat

I have an array of objects which looks like:
{
id: "1234",
general: {
title: "lorem",
body: "..."
}
}, ...
This data is being shown with an ng-repeat:
<ul>
<li ng-repeat="item in items track by $index">
<h2>{{item.general.title}}</h2>
<p>{{item.general.body}}</p>
</li>
</ul>
Now what I want to achieve is to add items to this list. Every 15 items I want to add a new item to the array to display in my ng-repeat. The item has a different structure:
<li>
<p>a text</p>
<a>a link</a>
</li>
So far I got this in my controller:
var addLinks = function addLinks(interval, array) {
var newArray = array.slice();
for(var i = interval - 1; i < array.length; i += interval) {
newArray.splice(i, 0, {
// Here comes the item to add
});
}
return newArray;
};
$scope.items = addLinks(15, articleService.articles);
My question is how do I add the item without just copying the html?
You could use ng-repeat-start and ng-repeat-end and only add the other element if you are on $index+1 % 15, like this:
<ul>
<li ng-repeat-start="item in vm.array track by $index">{{item.a}}</li>
<li ng-repeat-end ng-if="$index>0 && $index+1 % 15 == 0"></li>
</ul>
Here a plunkr: http://plnkr.co/edit/qwkXGcBcxlDy0hNHTvAo?p=preview
I would add additional properties to the item in question:
var addLinks = function addLinks(interval, array) {
//don't return a new array!
//var newArray = array.slice();
for(var i = interval - 1; i < array.length; i += interval) {
array[i].text = "foo";
array[i].link = "bar";
}
return array;
};
And in your view:
<ul>
<li ng-repeat="item in items track by $index">
<h2>{{item.general.title}}</h2>
<p>{{item.general.body}}</p>
<p ng-show="item.text">item.text</p>
<p ng-show="item.link">item.link</p>
</li>
</ul>

AngularJS Items selection and DOM manipulation

I have a list of items populated from a JSON, then i need to pick their values and populate another list of selected items, but, in case the particular item is already selected, add one more to count.
app.controller("NewPizza", function($scope, ingredients) {
$scope.selectedIngredients = [];
ingredients.get().then(function(response){
$scope.ingredients = response.data;
});
function _ingredientExist(id) {
return $scope.selectedIngredients.some(function(el) {
return el._id === id;
});
}
function _addMore(selectedIngredient) {
console.log(selectedIngredient)
}
$scope.addIngredient = function(selectedIngredient) {
if($scope.selectedIngredients.length == 0) {
$scope.selectedIngredients.push(selectedIngredient);
}else{
if(_ingredientExist(selectedIngredient._id)) {
_addMore(selectedIngredient._id);
return false;
}
$scope.selectedIngredients.push(selectedIngredient);
}
};
});
The result should be like this
Items to select
Cheese
Bacon
ham
Items selected
2 Cheese (In case user select cheese multiple times)
Bacon
HTML
<div class="">
<h1>New Pizza</h1>
<input ng-model="name"/>
<a>Save pizza</a>
<ul >
<li class="selectIngredients" ng-repeat="ingredient in ingredients" ng-click="addIngredient(ingredient)" id="{{ingredient._id}}">
{{ingredient.name}}
</li>
</ul>
<ul ng-model="selectedIngredients">
<li data-id="{{selectedIngredient._id}}" ng-repeat="selectedIngredient in selectedIngredients track by $index">
<span>1</span> {{selectedIngredient.name}}
</li>
</ul>
</div>
The problem is i dont know how exactly approach this feature because inside a controller DOM manipulation is considered a bad practice, but if i make a directive to deal with i dont know how to populate $scope.selectedIngredients properly.
Thanks!!
One way is that you can add a count to your items model, then copy that model and increment the number.
Created a fiddle: http://jsfiddle.net/ztnep7ay/
JS
var app = angular.module('itemsApp',[]);
app.controller('ItemsCtrl',function($scope) {
$scope.items = [
{name:'Cheese',num:0},
{name:'Bacon',num:0},
{name:'Ham',num:0}
];
$scope.my_items = angular.copy($scope.items);
$scope.addItem = function(item) {
var idx = $scope.items.indexOf(item);
var num = $scope.my_items[idx].num;
$scope.my_items[idx].num = num + 1;
};
$scope.removeItem = function(my_item) {
var idx = $scope.my_items.indexOf(my_item);
var num = my_item.num;
if (num > 0) {
$scope.my_items[idx].num = num -1;
}
};
});
HTML
<div ng-app="itemsApp" ng-controller="ItemsCtrl">
<h4>Available Items</h4>
<table>
<tr ng-repeat="i in items">
<td>{{i.name}}</td>
<td><button ng-click="addItem(i)">+</button></td>
</tr>
</table>
<hr>
<h4>My Items</h4>
<table>
<tr ng-repeat="i in my_items" ng-show="i.num > 0">
<td>{{i.name}} ({{i.num}})</td>
<td><button ng-click="removeItem(i)">Remove 1</button></td>
</tr>
</table>
</div>
You are right that it is considered wrong to update the DOM from a controller in the Angular world.
The reason for that is because you don't need to -- if you update your data - for example, the selectedIngredients array -- angular will update the DOM for you.
One way to accomplish this is to keep track of the count of each ingredient as well as what ingredient was added. You can do this without touching the Ingredient json that you get back from the server.
Then, when you change the count, angular will update the DOM for you.
Here's am example: Live Plnkr Example
HTML
<!DOCTYPE html>
<html ng-app="test">
<head>
<script data-require="angular.js#1.3.0-beta.5" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="PizzaCtrl">
<h4>Toppings</h4>
<ul>
<li ng-repeat="i in ingredients">
{{i.name}} - <a href ng-click="addIngredient(i)">Add</a>
</li>
</ul>
<h4>Selected Toppings</h4>
<ul>
<li ng-repeat="t in toppings">
{{t.ingredient.name}}
<span ng-if="t.count > 1">
x{{t.count}}
</span>
<a href ng-click="removeTopping(t, $index)">Remove</a>
</li>
</ul>
</body>
</html>
JS
angular.module('test', [])
.controller('PizzaCtrl', function($scope) {
/* let's protend we called ingredients.get() here */
$scope.ingredients = [
{ name: 'Cheese', id: 1 },
{ name: 'Bacon', id: 2 },
{ name: 'Ham', id: 3 }
];
/* this will hold both an Ingredient and a Count */
$scope.toppings = [];
/* Check if the ingredient already exists in toppings[], and if so,
* return it. */
function findTopping(ingredient) {
for(var i = 0; i < $scope.toppings.length; ++i) {
if ($scope.toppings[i].ingredient.id == ingredient.id) {
return $scope.toppings[i];
}
}
return null;
}
/* If the ingredient is already there, increment it's count,
* otherwise add it. */
$scope.addIngredient = function(ingredient) {
var t = findTopping(ingredient);
if (t) {
t.count++;
} else {
$scope.toppings.push({ingredient: ingredient, count: 1});
}
};
/* Opposite of the above! */
$scope.removeTopping = function(t, index) {
if (t.count > 1) {
t.count--;
} else {
$scope.toppings.splice(index, 1);
}
};
})

Resources