Calculating sum of repeated elements in AngularJS ng-repeat
Solution 1:
In Template
<td>Total: {{ getTotal() }}</td>
In Controller
$scope.getTotal = function(){
var total = 0;
for(var i = 0; i < $scope.cart.products.length; i++){
var product = $scope.cart.products[i];
total += (product.price * product.quantity);
}
return total;
}
Solution 2:
This is also working both the filter and normal list. The first thing to create a new filter for the sum of all values from the list, and also given solution for a sum of the total quantity. In details code check it fiddler link.
angular.module("sampleApp", [])
.filter('sumOfValue', function () {
return function (data, key) {
if (angular.isUndefined(data) || angular.isUndefined(key))
return 0;
var sum = 0;
angular.forEach(data,function(value){
sum = sum + parseInt(value[key], 10);
});
return sum;
}
}).filter('totalSumPriceQty', function () {
return function (data, key1, key2) {
if (angular.isUndefined(data) || angular.isUndefined(key1) || angular.isUndefined(key2))
return 0;
var sum = 0;
angular.forEach(data,function(value){
sum = sum + (parseInt(value[key1], 10) * parseInt(value[key2], 10));
});
return sum;
}
}).controller("sampleController", function ($scope) {
$scope.items = [
{"id": 1,"details": "test11","quantity": 2,"price": 100},
{"id": 2,"details": "test12","quantity": 5,"price": 120},
{"id": 3,"details": "test3","quantity": 6,"price": 170},
{"id": 4,"details": "test4","quantity": 8,"price": 70}
];
});
<div ng-app="sampleApp">
<div ng-controller="sampleController">
<div class="col-md-12 col-lg-12 col-sm-12 col-xsml-12">
<label>Search</label>
<input type="text" class="form-control" ng-model="searchFilter" />
</div>
<div class="col-md-12 col-lg-12 col-sm-12 col-xsml-12">
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2">
<h4>Id</h4>
</div>
<div class="col-md-4 col-lg-4 col-sm-4 col-xsml-4">
<h4>Details</h4>
</div>
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2 text-right">
<h4>Quantity</h4>
</div>
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2 text-right">
<h4>Price</h4>
</div>
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2 text-right">
<h4>Total</h4>
</div>
<div ng-repeat="item in resultValue=(items | filter:{'details':searchFilter})">
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2">{{item.id}}</div>
<div class="col-md-4 col-lg-4 col-sm-4 col-xsml-4">{{item.details}}</div>
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2 text-right">{{item.quantity}}</div>
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2 text-right">{{item.price}}</div>
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2 text-right">{{item.quantity * item.price}}</div>
</div>
<div colspan='3' class="col-md-8 col-lg-8 col-sm-8 col-xsml-8 text-right">
<h4>{{resultValue | sumOfValue:'quantity'}}</h4>
</div>
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2 text-right">
<h4>{{resultValue | sumOfValue:'price'}}</h4>
</div>
<div class="col-md-2 col-lg-2 col-sm-2 col-xsml-2 text-right">
<h4>{{resultValue | totalSumPriceQty:'quantity':'price'}}</h4>
</div>
</div>
</div>
</div>
check this Fiddle Link
Solution 3:
Realizing this answered long ago, but wanted to post different approach not presented...
Use ng-init
to tally your total. This way, you do not have to iterate in the HTML and iterate in the controller. In this scenario, I think this is a cleaner/simpler solution. (If the tallying logic was more complex, I definitely would recommend moving the logic to the controller or service as appropriate.)
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Price</th>
</tr>
<tr ng-repeat="product in cart.products">
<td>{{product.name}}</td>
<td>{{product.quantity}}</td>
<td ng-init="itemTotal = product.price * product.quantity; controller.Total = controller.Total + itemTotal">{{itemTotal}} €</td>
</tr>
<tr>
<td></td>
<td>Total :</td>
<td>{{ controller.Total }}</td> // Here is the total value of my cart
</tr>
Of course, in your controller, simply define/initialize your Total
field:
// random controller snippet
function yourController($scope..., blah) {
var vm = this;
vm.Total = 0;
}
Solution 4:
You can calculate total inside ng-repeat
follow:
<tbody ng-init="total = 0">
<tr ng-repeat="product in products">
<td>{{ product.name }}</td>
<td>{{ product.quantity }}</td>
<td ng-init="$parent.total = $parent.total + (product.price * product.quantity)">${{ product.price * product.quantity }}</td>
</tr>
<tr>
<td>Total</td>
<td></td>
<td>${{ total }}</td>
</tr>
</tbody>
Check result here: http://plnkr.co/edit/Gb8XiCf2RWiozFI3xWzp?p=preview
In case automatic update result: http://plnkr.co/edit/QSxYbgjDjkuSH2s5JBPf?p=preview (Thanks – VicJordan)
Solution 5:
This is my solution
sweet and simple custom filter:
(but only related to simple sum of values, not sum product, I've made up sumProduct
filter and appended it as edit to this post).
angular.module('myApp', [])
.filter('total', function () {
return function (input, property) {
var i = input instanceof Array ? input.length : 0;
// if property is not defined, returns length of array
// if array has zero length or if it is not an array, return zero
if (typeof property === 'undefined' || i === 0) {
return i;
// test if property is number so it can be counted
} else if (isNaN(input[0][property])) {
throw 'filter total can count only numeric values';
// finaly, do the counting and return total
} else {
var total = 0;
while (i--)
total += input[i][property];
return total;
}
};
})
JS Fiddle
EDIT: sumProduct
This is sumProduct
filter, it accepts any number of arguments. As a argument it accepts name of the property from input data, and it can handle nested property (nesting marked by dot: property.nested
);
- Passing zero argument returns length of input data.
- Passing just one argument returns simple sum of values of that properties.
- Passing more arguments returns sum of products of values of passed properties (scalar sum of properties).
here's JS Fiddle and the code
angular.module('myApp', [])
.filter('sumProduct', function() {
return function (input) {
var i = input instanceof Array ? input.length : 0;
var a = arguments.length;
if (a === 1 || i === 0)
return i;
var keys = [];
while (a-- > 1) {
var key = arguments[a].split('.');
var property = getNestedPropertyByKey(input[0], key);
if (isNaN(property))
throw 'filter sumProduct can count only numeric values';
keys.push(key);
}
var total = 0;
while (i--) {
var product = 1;
for (var k = 0; k < keys.length; k++)
product *= getNestedPropertyByKey(input[i], keys[k]);
total += product;
}
return total;
function getNestedPropertyByKey(data, key) {
for (var j = 0; j < key.length; j++)
data = data[key[j]];
return data;
}
}
})