Calculating sum of repeated elements in AngularJS ng-repeat
Calculating sum of repeated elements in AngularJS ng-repeat
The script below displays a shop cart using ng-repeat
. For each element in the array, it shows the item name, its amount and the subtotal (product.price * product.quantity
).
ng-repeat
product.price * product.quantity
What is the simplest way for calculating the total price of repeated elements?
<table>
<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>product.price * product.quantity €</td>
</tr>
<tr>
<td></td>
<td>Total :</td>
<td></td> <!-- Here is the total value of my cart -->
</tr>
</table>
See also: stackoverflow.com/a/25667437/59087
– Dave Jarvis
Sep 14 '15 at 2:10
stackoverflow.com/questions/22731145/…
– Rajamohan Anguchamy
Jul 14 '16 at 7:02
Why dont you use tfoot-tag?
– Pascal
Nov 5 '16 at 9:29
18 Answers
18
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;
one downside to this is that it iterates over the collection twice. this is fine for small collections, but what if the collection is rather large? it seems like in ng-repeat there should be a way to have a running sum on a given object field.
– icfantv
Aug 6 '14 at 16:14
Plus, this is not updated if the product list is filtered
– Pascamel
Aug 19 '14 at 15:40
@Pascamel Check my answer(stackoverflow.com/questions/22731145/…) i think that that one working for what you asking about with filter
– Rajamohan Anguchamy
Sep 17 '14 at 11:54
exactly what I was looking for when I landed on that question, thanks for the heads up @RajaShilpa!
– Pascamel
Sep 17 '14 at 12:43
The main issue with this solution is that the total will be recalculated with every digest, because it is a function call.
– Marc Durdin
Jul 30 '15 at 0:12
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)
).filter('totalSumPriceQty', function ()
return function (data, key1, key2)
).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
];
);
check this Fiddle Link
Hey I am getting
'undefined'
when using the resultValue
, but if i use items
it works fine, any ideas..??– Salal Aslam
May 15 '15 at 9:28
'undefined'
resultValue
items
Where first you check the following code "resultValue=(items | filter:'details':searchFilter)", because all filter values store in that variable "resultValue". i think you mistaken for that or () this, verify once again.
– Rajamohan Anguchamy
May 15 '15 at 9:34
If i use
items
it will not work with filters, help!– Salal Aslam
May 15 '15 at 9:36
items
my code is like this
ng-repeat="campaign in filteredCampaigns=(campaigns | filter:'name':q)"
and number: 2
– Salal Aslam
May 15 '15 at 9:37
ng-repeat="campaign in filteredCampaigns=(campaigns | filter:'name':q)"
number: 2
yes, because items is not yet filtered, after the filter is happened that result is must to store to any other model and should use that model only. In my sample i used "resultValue" model.
– Rajamohan Anguchamy
May 15 '15 at 9:38
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.)
ng-init
<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:
Total
// random controller snippet
function yourController($scope..., blah)
var vm = this;
vm.Total = 0;
This is definitely the most angular way. Simple, readable and declarative. So, the logic it represents remains where it belongs.
– Daniel Leiszen
Feb 11 '15 at 22:08
This method hides the calculation in the cell presentation, which is easy to understand here, but gets pretty messy with complex tables.
– Marc Durdin
Jul 30 '15 at 0:13
The other issue with this is that it doesn't have two way binding either.
– Paul Carlton
Nov 8 '15 at 23:56
You can calculate total inside ng-repeat
follow:
ng-repeat
<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)
this wont work when the list is filtered -
tbody
is initialized only once, but tr
each time the list is filtered, resulting in incorrect sum– Zbynek
May 23 '16 at 10:43
tbody
tr
Can you make an example on plnkr or jsfiddle?
– Huy Nguyen
May 23 '16 at 15:34
here it is: plnkr.co/edit/AEtIW9xYlyZkZPdqweGq?p=preview
– Zbynek
May 23 '16 at 16:17
Hmm, yes, it's not work in filter because filter here just show/hide on view, not update
$scope
– Huy Nguyen
May 24 '16 at 1:36
$scope
@HuyNguyen, I have edited your above code. Please check here : plnkr.co/edit/QSxYbgjDjkuSH2s5JBPf?p=preview . Here what I want is the if user change the quantity then 4th column (price * quantity) should be updated automatically. Could you please have a look into this. Thanks
– VicJordan
Aug 4 '17 at 4:42
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).
sumProduct
angular.module('myApp', )
.filter('total', function ()
return function (input, property) ;
)
JS Fiddle
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
);
sumProduct
property.nested
here's JS Fiddle and the code
angular.module('myApp', )
.filter('sumProduct', function()
return function (input)
)
JS Fiddle
Simple Solution
Here is a simple solution. No additional for loop required.
HTML part
<table ng-init="ResetTotalAmt()">
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Price</th>
</tr>
<tr ng-repeat="product in cart.products">
<td ng-init="CalculateSum(product)">product.name</td>
<td>product.quantity</td>
<td>product.price * product.quantity €</td>
</tr>
<tr>
<td></td>
<td>Total :</td>
<td>cart.TotalAmt</td> // Here is the total value of my cart
</tr>
</table>
Script Part
$scope.cart.TotalAmt = 0;
$scope.CalculateSum= function (product)
$scope.cart.TotalAmt += (product.price * product.quantity);
//It is enough to Write code $scope.cart.TotalAmt =0; in the function where the cart.products get allocated value.
$scope.ResetTotalAmt = function (product)
$scope.cart.TotalAmt =0;
Another way of solving this, extending from Vaclav's answer to solve this particular calculation — i.e. a calculation on each row.
.filter('total', function ()
return function (input, property)
var i = input instanceof Array ? input.length : 0;
if (typeof property === 'undefined' ;
)
To do this with a calculation, just add a calculation function to your scope, e.g.
$scope.calcItemTotal = function(v) return v.price*v.quantity; ;
You would use currency
in your HTML code. This has the advantage of not being called for every digest, because it uses filters, and can be used for simple or complex totals.
currency
JSFiddle
This is a simple way to do this with ng-repeat and ng-init to aggregate all the values and extend the model with a item.total property.
<table>
<tr ng-repeat="item in items" ng-init="setTotals(item)">
<td>item.name</td>
<td>item.quantity</td>
<td> number:2</td>
<td> number:2</td>
</tr>
<tr class="bg-warning">
<td>Totals</td>
<td>invoiceCount</td>
<td></td>
<td> number:2</td>
</tr>
</table>
The ngInit directive calls the set total function for each item.
The setTotals function in the controller calculates each item total. It also uses the invoiceCount and invoiceTotal scope variables to aggregate (sum) the quantity and total for all items.
$scope.setTotals = function(item)
if (item)
item.total = item.quantity * item.unitCost;
$scope.invoiceCount += item.quantity;
$scope.invoiceTotal += item.total;
for more information and demo look at this link:
http://www.ozkary.com/2015/06/angularjs-calculate-totals-using.html
Links to your blog post that might go link dead are discouraged on StackOverlow. Also, when I look at the page I get a 502 Bad Gateway error in the middle of your page. Answer the question right here not a link to somewhere else.
– Rick Glos
Aug 12 '15 at 21:55
I prefer elegant solutions
In Template
<td>Total: totalSum </td>
In Controller
$scope.totalSum = Object.keys(cart.products).map(function(k)
return +cart.products[k].price;
).reduce(function(a,b) return a + b ,0);
If you're using ES2015 (aka ES6)
$scope.totalSum = Object.keys(cart.products)
.map(k => +cart.products[k].price)
.reduce((a, b) => a + b);
You may try using services of angular js, it has worked for me..giving the code snippets below
Controller code:
$scope.total = 0;
var aCart = new CartService();
$scope.addItemToCart = function (product)
aCart.addCartTotal(product.Price);
;
$scope.showCart = function ()
$scope.total = aCart.getCartTotal();
;
Service Code:
app.service("CartService", function ()
Total = ;
Total.length = 0;
return function ()
this.addCartTotal = function (inTotal)
Total.push( inTotal);
this.getCartTotal = function ()
var sum = 0;
for (var i = 0; i < Total.length; i++)
sum += parseInt(Total[i], 10);
return sum;
;
);
here is my solution to this problem:
<td>Total: calculateTotal() </td>
script
$scope.calculateVAT = function ()
return $scope.cart.products.reduce((accumulator, currentValue) => accumulator + (currentValue.price * currentValue.quantity), 0);
;
reduce will execute for each product in products array.
Accumulator is the total accumulated amount, currentValue is the current element of the array and the 0 in the last is the initial value
I expanded a bit on RajaShilpa's answer. You can use syntax like:
object
so that you can access an object's child object. Here is the code for the filter:
.filter('sumOfTwoValues', function ()
return function (data, key1, key2)
);
Taking Vaclav's answer and making it more Angular-like:
angular.module('myApp').filter('total', ['$parse', function ($parse)
return function (input, property)
var i = input instanceof Array ? input.length : 0,
p = $parse(property);
if (typeof property === 'undefined' ;
]);
This gives you the benefit of even accessing nested and array data:
data
In html
<b class="text-primary">Total Amount: $ data.allTicketsTotalPrice() </b>
in javascript
app.controller('myController', function ($http)
var vm = this;
vm.allTicketsTotalPrice = function ()
var totalPrice = 0;
angular.forEach(vm.ticketTotalPrice, function (value, key)
totalPrice += parseFloat(value);
);
return totalPrice.toFixed(2);
;
);
Huy Nguyen's answer is almost there. To make it work, add:
ng-repeat="_ in [ products ]"
...to the line with ng-init. The list always has a single item, so Angular will repeat the block exactly once.
Zybnek's demo using filtering can be made to work by adding:
ng-repeat="_ in [ [ products, search ] ]"
See http://plnkr.co/edit/dLSntiy8EyahZ0upDpgy?p=preview.
You can use a custom Angular filter that takes the dataset object array and the key in each object to sum. The filter can then return the sum:
.filter('sumColumn', function()
return function(dataSet, columnToSum)
let sum = 0;
for(let i = 0; i < dataSet.length; i++)
sum += parseFloat(dataSet[i][columnToSum])
return sum;
;
)
Then in your table to sum a column you can use:
<th> sumColumn: 'keyInObjectToSum' </th>
**Angular 6: Grand Total**
**<h2 align="center">Usage Details Of profile$.firstName</h2>
<table align ="center">
<tr>
<th>Call Usage</th>
<th>Data Usage</th>
<th>SMS Usage</th>
<th>Total Bill</th>
</tr>
<tr>
<tr *ngFor="let user of bills$">
<td> user.callUsage</td>
<td> user.dataUsage </td>
<td> user.smsUsage </td>
<td>user.callUsage *2 + user.dataUsage *1 + user.smsUsage *1</td>
</tr>
<tr>
<th> </th>
<th>Grand Total</th>
<th></th>
<td>total( bills$)</td>
</tr>
</table>**
**Controller:**
total(bills)
var total = 0;
bills.forEach(element =>
total = total + (element.callUsage * 2 + element.dataUsage * 1 + element.smsUsage * 1);
);
return total;
From Review: Welcome to Stack Overflow! Please don't answer just with source code. Try to provide a nice description about how your solution works. See: How do I write a good answer?. Thanks
– sɐunıɔןɐqɐp
Oct 4 '18 at 6:28
After reading all the answers here - how to summarize grouped information, i decided to skip it all and just loaded one of the SQL javascript libraries. I'm using alasql, yeah it takes a few secs longer on load time but saves countless time in coding and debugging, Now to group and sum() I just use,
$scope.bySchool = alasql('SELECT School, SUM(Cost) AS Cost from ? GROUP BY School',[restResults]);
I know this sounds like a bit of a rant on angular/js but really SQL solved this 30+ years ago and we shouldn't have to re-invent it within a browser.
This is just pretty awful. Just wow SMH - I will let other people down vote. My mouth is wide open with this answer.....
– Tom Stickel
Jan 5 '17 at 7:45
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
angular.forEach($scope.cart.products, function(filterObj , filterKey) $scope.total += filterObj.product.price * filterObj.product.quantity; );
– Gery
Jun 15 '15 at 13:48