Calculating sum of repeated elements in AngularJS

2019-01-01 07:03发布

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

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>

18条回答
无与为乐者.
2楼-- · 2019-01-01 07:13

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;
            }
        }
    })

JS Fiddle

查看更多
看风景的人
3楼-- · 2019-01-01 07:13

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' || i === 0) {
                return i;
            } else if (typeof property === 'function') {
                var total = 0; 
                while (i--)
                    total += property(input[i]);
                return total;
            } else if (isNaN(input[0][property])) {
                throw 'filter total can count only numeric values';
            } else {
                var total = 0;
                while (i--)
                    total += input[i][property];
                return total;
            }
        };
    })

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 {{ datas|total:calcItemTotal|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.

JSFiddle

查看更多
素衣白纱
4楼-- · 2019-01-01 07:20

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)

查看更多
浅入江南
5楼-- · 2019-01-01 07:22

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);
查看更多
姐姐魅力值爆表
6楼-- · 2019-01-01 07:23

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}
        ];
    });

check this Fiddle Link

查看更多
临风纵饮
7楼-- · 2019-01-01 07:23

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>{{item.unitCost | number:2}}</td>
                    <td>{{item.total | number:2}}</td>
</tr>
<tr class="bg-warning">
                    <td>Totals</td>
                    <td>{{invoiceCount}}</td>
                    <td></td>                    
                    <td>{{invoiceTotal | 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

查看更多
登录 后发表回答