Cálculo de la sum de elementos repetidos en AngularJS ng-repeat

La siguiente secuencia de comandos muestra un carrito de la compra usando ng-repeat . Para cada elemento de la matriz, muestra el nombre del elemento, su cantidad y el subtotal ( product.price * product.quantity ).

¿Cuál es la forma más simple de calcular el precio total de los elementos repetidos?

 
Product Quantity Price
{{product.name}} {{product.quantity}} {{product.price * product.quantity}} €
Total :

En la plantilla

 Total: {{ getTotal() }} 

En el controlador

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

Esto también está funcionando tanto en el filtro como en la lista normal. Lo primero es crear un nuevo filtro para la sum de todos los valores de la lista, y también una solución dada para una sum de la cantidad total. En detalles, verifique el enlace del violinista .

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

revisa este enlace Fiddle

Al darse cuenta de esto respondió hace mucho tiempo, pero quería publicar un enfoque diferente no presentado …

Usa ng-init para contar tu total. De esta forma, no tiene que iterar en el HTML e iterar en el controlador. En este escenario, creo que esta es una solución más simple / más limpia. (Si la lógica de conteo era más compleja, definitivamente recomendaría mover la lógica al controlador o servicio según corresponda).

   Product Quantity Price   {{product.name}} {{product.quantity}} {{itemTotal}} €    Total : {{ controller.Total }} // Here is the total value of my cart  

Por supuesto, en su controlador, simplemente defina / inicialice su campo Total :

 // random controller snippet function yourController($scope..., blah) { var vm = this; vm.Total = 0; } 

Puede calcular el total de ng-repeat siguiente seguimiento:

   {{ product.name }} {{ product.quantity }} ${{ product.price * product.quantity }}   Total  ${{ total }}   

Verifique el resultado aquí: http://plnkr.co/edit/Gb8XiCf2RWiozFI3xWzp?p=preview

En caso de resultado de la actualización automática: http://plnkr.co/edit/QSxYbgjDjkuSH2s5JBPf?p=preview (Thanks – VicJordan)

Esta es mi solución

filtro personalizado dulce y simple:

(pero solo relacionado con la simple sum de valores, no el producto de la sum, hice el filtro sumProduct y lo sumProduct como edición a esta publicación).

 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

EDITAR: sumProducto

Este es sumProduct filtro de sumProduct , acepta cualquier cantidad de argumentos. Como argumento acepta el nombre de la propiedad de los datos de entrada, y puede manejar la propiedad anidada (anidación marcada por dot: property.nested );

  • Pasar cero argumento devuelve la longitud de los datos de entrada.
  • Pasar solo un argumento devuelve una sum simple de valores de esas propiedades.
  • Al pasar más argumentos, se devuelve la sum de productos de valores de propiedades pasadas (sum escalar de propiedades).

aquí está JS Fiddle y el código

 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

Solución simple

Aquí hay una solución simple. No se requiere bucle adicional.

Parte HTML

   // Here is the total value of my cart 
Product Quantity Price
{{product.name}} {{product.quantity}} {{product.price * product.quantity}} €
Total : {{cart.TotalAmt}}

Parte del script

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

Otra forma de resolver esto, se extiende desde la respuesta de Vaclav para resolver este cálculo particular, es decir, un cálculo en cada fila.

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

Para hacer esto con un cálculo, simplemente agregue una función de cálculo a su scope, por ejemplo

 $scope.calcItemTotal = function(v) { return v.price*v.quantity; }; 

Deberías usar {{ datas|total:calcItemTotal|currency }} en tu código HTML. Esto tiene la ventaja de no ser llamado para cada resumen, porque usa filtros y puede usarse para totales simples o complejos.

JSFiddle

Esta es una forma simple de hacer esto con ng-repeat y ng-init para agregar todos los valores y extender el modelo con una propiedad item.total.

 
{{item.name}} {{item.quantity}} {{item.unitCost | number:2}} {{item.total | number:2}}
Totals {{invoiceCount}} {{invoiceTotal | number:2}}

La directiva ngInit llama a la función de conjunto total para cada elemento. La función setTotals en el controlador calcula el total de cada artículo. También utiliza las variables invoiceCount y invoiceTotal scope para agregar (sumr) la cantidad y el total de todos los artículos.

 $scope.setTotals = function(item){ if (item){ item.total = item.quantity * item.unitCost; $scope.invoiceCount += item.quantity; $scope.invoiceTotal += item.total; } } 

para más información y demostración mira este enlace:

http://www.ozkary.com/2015/06/angularjs-calculate-totals-using.html

Prefiero soluciones elegantes

En la plantilla

 Total: {{ totalSum }} 

En el controlador

 $scope.totalSum = Object.keys(cart.products).map(function(k){ return +cart.products[k].price; }).reduce(function(a,b){ return a + b },0); 

Si está utilizando ES2015 (también conocido como ES6)

 $scope.totalSum = Object.keys(cart.products).map(function(k){ return +cart.products[k].price; }).reduce((a,b) => a + b); 

Me expandí un poco en la respuesta de RajaShilpa. Puedes usar syntax como:

 {{object | sumOfTwoValues:'quantity':'products.productWeight'}} 

para que pueda acceder al objeto hijo de un objeto. Aquí está el código para el filtro:

 .filter('sumOfTwoValues', function () { return function (data, key1, key2) { if (typeof (data) === 'undefined' || typeof (key1) === 'undefined' || typeof (key2) === 'undefined') { return 0; } var keyObjects1 = key1.split('.'); var keyObjects2 = key2.split('.'); var sum = 0; for (i = 0; i < data.length; i++) { var value1 = data[i]; var value2 = data[i]; for (j = 0; j < keyObjects1.length; j++) { value1 = value1[keyObjects1[j]]; } for (k = 0; k < keyObjects2.length; k++) { value2 = value2[keyObjects2[k]]; } sum = sum + (value1 * value2); } return sum; } }); 

Tomando la respuesta de Vaclav y haciéndola más parecida a Angular:

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

Esto le da la ventaja de incluso acceder a los datos nesteds y de matriz:

 {{data | total:'values[0].value'}} 

En html

 Total Amount: ${{ data.allTicketsTotalPrice() }} 

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

La respuesta de Huy Nguyen está casi allí. Para que funcione, agregue:

 ng-repeat="_ in [ products ]" 

… a la línea con ng-init. La lista siempre tiene un solo elemento, por lo que Angular repetirá el bloque exactamente una vez.

Se puede hacer que funcione la demostración de Zybnek usando el filtrado agregando:

 ng-repeat="_ in [ [ products, search ] ]" 

Ver http://plnkr.co/edit/dLSntiy8EyahZ0upDpgy?p=preview .

aquí está mi solución a este problema:

 Total: {{ calculateTotal() }} 

guión

 $scope.calculateVAT = function () { $scope.cart.products.reduce((accumulator, currentValue) => accumulator + (currentValue.price * currentValue.quantity), 0); }; 

reducir se ejecutará para cada producto en la matriz de productos. Acumulador es la cantidad total acumulada, currentValue es el elemento actual de la matriz y el 0 en el último es el valor inicial

Después de leer todas las respuestas aquí, cómo resumir la información agrupada, decidí omitirlo todo y solo cargar una de las bibliotecas SQL javascript. Estoy usando alasql, sí, tarda unos segundos más en el tiempo de carga, pero ahorra un tiempo incontable en la encoding y la depuración, ahora para agrupar y sumr () solo uso,

 $scope.bySchool = alasql('SELECT School, SUM(Cost) AS Cost from ? GROUP BY School',[restResults]); 

Sé que esto suena como una farsa sobre angular / js, pero realmente solucionó SQL hace más de 30 años y no deberíamos tener que reinventarlo en un navegador.