AngularJS calculate a dynamic number of variables in braces - angularjs

I want to calculate the total sum, of a dynamic number of variables within angular's brace syntax.
The for loop I would normally use would look like :
var total = 0;
for(x=0; x< objects.length; x++) {
total += objects[x].num;
}
I would like to recreate this as {{ total }} or {{total()}} and have the total update when ever one of the variables is changed.

Here is what you are looking for: JSFiddle
HTML
<div ng-app="myApp" ng-controller="ctrl" ng-strict-di>
{{total()}}<br/>
<input type="text" ng-model="item.num" ng-repeat="item in objects"/>
</div>
JS:
angular.module('myApp',[]).controller('ctrl',function($scope) {
angular.extend($scope,{
objects:[
{num:7},
{num:8},
{num:9}
],
total : function(){
var res = 0;
for (var i=0;i<$scope.objects.length;i++)
res+=parseInt($scope.objects[i].num);
return res;
}
});
});
UPDATE
To test if it's updating the total on object's num update play with code like this:
setInterval(function() {
$scope.objects[1].num++;
$scope.$apply();
},500); //increase num of 2nd object by 1, every half of second
To test if it's still updating when adding new objects, play with code like that:
setInterval(function() {
$scope.objects.push({num:Math.floor(Math.random()*100)});
},5000); //Add new object, with random num: value, every 5 seconds

Related

Angular.js : Calculating the total of a list of numbers

I need to have a list of numbers display with the total. When someone enters a new number into an input box, the number will be added to the list displayed and the total will change accordingly.
using angular.js...
While itamar's answer works without any doubt, you can write this like this (a bit cleaner in my opinion) :
Controller
Use reduce for total computation :
$scope.numbers = [1,2,3];
$scope.updateTotal = function () {
$scope.total = numbers.reduce(function (total, number) {
if (number) {
total += number;
}
return total;
}, 0);
};
Markup
<span>{{ total }}</span>
<ul>
<li ng-repeat="number in numbers">
<input type="number" ng-model="number" ng-change="updateTotal()">
</li>
</ul>
Since you know when the total value should get updated, better use ng-change here so the computation is only done when needed.
On a side note, you can use type="number" in the input so only numbers can be entered.
Welcome to StackOverflow - next time please provide some code you wrote yourself. In the meantime - I wrote yours for you :-)
JS in your controller:
$scope.numbers = [1,2,3];
$scope.getTotals = function(){
var total = 0;
total = $scope.numbers.reduce(function(prev, curr) {
return prev + curr;
});
return total;
}
HTML:
<ul>
<li ng-repeat="number in numbers"><input ng-model="number"></li>
<li>{{getTotals()}}</li>
</ul>

Filtering through dropdown in Angular

I have a drop down which has options of price range (generating dynamically depending upon the prices of cars in my model). On selecting a price-range, let's say '3000 to 4000', I want cars belonging to those price range to appear.
Here's my change event listener function controller:
$('#priceRange').change(function(){
$scope.init(); //init updates cars[] with all the 20 objects
var min, max;
var index = $('#priceRange :selected').index();
/*algo to find min and max range*/
if(index == 0){
min = $scope.minRange;
max = $scope.maxRange;
}else{
min = (index-1)*1000;
if(index*1000 == $scope.maxRange){
max = index*1000;
}else{
max = index*1000-1;
}
}
//For 3000 to 4000, I am passing min = 3000, max = 4000 and all the cars which then priceFilter function updates $scope.cars with cars belonging to that range
$scope.cars = $scope.priceFilter($scope.cars, min, max);
});
$scope.priceFilter = function(items, min, max) {
var filtered = [];
angular.forEach(items, function(item, key) {
if(item.price <= max && item.price >= min){
filtered.push(item);
}
});
return filtered;
};
But these updated $scope.cars doesn't reflect in my view as I am expecting. Here's the markup:
<label>Make: </label> <input ng-model="search.make">
<select id="priceRange"><option>All</option></select>
<div class="col-md-3" ng-repeat="car in cars | filter:search">
<img class="carImg" src="{{car.imgPath}}" alt="{{car.make}}">
<div class="col-sm-8">
<h4>{{car.make}} {{car.model}}</h4>
<h4>{{car.year}}, {{car.price | currency}}</h4>
</div>
</div>
As the change is handled via jQuery, Angular has no notion of it, unless you wake up Angular explicitly by calling $apply():
$scope.cars = $scope.priceFilter($scope.cars, min, max);
$scope.$apply();
See also my Fiddle: http://jsfiddle.net/masa671/hs9vu0r2/
I agree with #dfsq that, with Angular, jQuery is usually not the best way for handling this type of functionality.

How can I use two ng-repeats to iterate through an array that might have a different length?

I have an ng-repeat and another ng-repeat inside that like this:
<div ng-repeat="i in [0,1,2,3,4]">
<div ng-repeat="j in [0,1,2,3,4,5,6,7,8,9]" ng-init="row=qs[i * 10 + j + 1]'">
<div ng-click="getQuestion(row.number)">
{{ row.number }}
</div>
</div>
</div>
The code iterates through 50 members of the array qs which is defined on my $scope in the controller loaded there. The code above nicely creates 5 rows of ten <div>s to hold details from each array element.
It's possible however that the qs array could have just 2 or perhaps 12 or 20 or any number of elements. If it was 2 elements I would want to have one row of two <div>s. If it was twelve I would want to have one row of 10 <div>s and one row below that of two <div>s.
Is there a way that I could make this code more flexible so that it could cope with a different array lengths? In particular I realize that for example I could take the array length and if it was 25 then I know I would have to have my ng repeat of i go through [0,1,2] but how can I create the [0,1,2] from 25 ?
You could create a simple filter to generate array based on provided length.
.filter('range', function() {
return function(input, range) {
if (range < 1) return [];
return Array.apply(null, {
length: range
}).map(Number.call, Number);
}
});
and just use it as:-
<div ng-repeat="i in []|range:qns.length/10">
angular.module('app', []).controller('ctrl', function($scope) {
$scope.count = 100;
})
.filter('range', function() {
return function(input, range) {
if (range < 1) return [];
return Array.apply(null, {
length: range
}).map(Number.call, Number);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-repeat="i in []|range:count/10">
Hey
</div>
</div>

Angular JS using ng-repeat to get in 3's then create a new row

I am trying to create a table using ng-repeat, to 3 items in each row, then create a new row after, etc.
I am using a div span3, but it is not wrapping my bootstrap badges.
Any help would be great.
Thanks
Using a range filter you can do nested repeats- the inner counting off the 3 items in your row and the outer counting off your rows (i.e. groups of three):
<div class="row" ng-repeat="n in [] | range:(data.length/3)+1">
<span ng-repeat='item in data.slice($index*3,($index*3+3))'>
{{item}}
</span>
</div>
and the associated range filter which returns an array that goes from 0 up to the specified parameter:
app.filter('range', function () {
return function (input, total) {
total = parseInt(total);
for (var i = 0; i < total; i++) {
input.push(i);
}
return input;
};

Prepare array of objects before passing it to Angularjs ng-repeat to avoid '10 $digest() iterations reached' error

I have a following ng-repeat in my view:
<div ng-repeat="field in fields">
{{field.someValue}}
</div>
The content of fields needs to be preprocessed before it is given to the view. So in my controller I have a function that loops through the fields object and adds some keys and removes some keys. A simplified pseudocode would look like that
myApp.controller('FieldsController', function($scope) {
$scope.fields = loadFieldsFromResource();
var i=0;
for(i = 0; i < $scope.fields.length; i++) {
if ($scope.fields[i].someProperty > maxValue) {
// Remove an item from the array
$scope.fields.splice(i,1);
}
else if ($scope.fields[i].someProperty < minValue) {
// Add an item to the array
$scope.fields.splice(i,0,createNewField());
}
}
})
Now this produces correct output but gives me the 10 $digest() iterations reached. error.
How can I make it work? (I only need the preprocessing done on init).
I've tried to copy the fields with angular.copy() to a temporary variable. Do the preprocessing on it and then assigning it to the fields variable but still I get the same error.
Is there a way of doing this sort of preprocessing outside of the Angular watch before I give it to the view?
Could you use ng-init?
<div ng-init="processFields()" ng-repeat="field in fields">
{{field.someValue}}
</div>
And then in your controller you could do the following:
myApp.controller('FieldsController', function ($scope) {
$scope.fields = [];
$scope.processFields = function () {
var fields = loadFieldsFromResource();
var i = 0;
for (i = 0; i < fields.length; i++) {
if (fields[i].someProperty > maxValue) {
// Remove an item from the array
fields.splice(i, 1);
}
else if (fields[i].someProperty < minValue) {
// Add an item to the array
fields.splice(i, 0, createNewField());
}
}
$scope.fields = fields;
}
})
I don't know your entire situation but I have few places where I need things loaded and sorted before I display.

Resources