web-dev-qa-db-fra.com

Calcul de la somme des éléments répétés dans AngularJS ng-repeat

Le script ci-dessous affiche un panier d'achat utilisant ng-repeat. Pour chaque élément du tableau, il affiche le nom de l'élément, sa quantité et le sous-total (product.price * product.quantity).

Quelle est la manière la plus simple de calculer le prix total des éléments répétés?

<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>
106
keepthepeach

Dans le modèle

<td>Total: {{ getTotal() }}</td>

Dans le contrôleur

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

Cela fonctionne aussi bien sur le filtre que sur la liste normale. La première chose à créer un nouveau filtre pour la somme de toutes les valeurs de la liste, et également une solution pour une somme de la quantité totale. Dans le code de détails, vérifiez-le lien de fiddler .

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>

vérifier ceci Lien de violon

57
Rajamohan Anguchamy

Réaliser cela répond depuis longtemps, mais je voulais poster une approche différente non présentée ...

Utilisez ng-init pour calculer votre total. De cette façon, vous n'avez pas besoin d'itérer dans le code HTML ni dans le contrôleur. Dans ce scénario, je pense que c'est une solution plus propre/plus simple. (Si la logique de décompte était plus complexe, je recommanderais certainement de déplacer la logique vers le contrôleur ou le service, selon le cas.)

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

Bien sûr, dans votre contrôleur, définissez/initialisez simplement votre champ Total:

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

Vous pouvez calculer le total dans ng-repeat comme suit:

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

Vérifiez le résultat ici: http://plnkr.co/edit/Gb8XiCf2RWiozFI3xWzp?p=preview

En cas de résultat de mise à jour automatique: http://plnkr.co/edit/QSxYbgjDjkuSH2s5JBPf?p=preview (Merci - VicJordan)

17
Huy Nguyen

C'est ma solution

filtre personnalisé doux et simple:

(mais uniquement lié à la somme simple des valeurs, pas à la somme du produit, j'ai composé le filtre sumProduct et l'a ajouté en tant que modification à ce message).

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

Ceci est le filtre sumProduct, il accepte un nombre quelconque d'arguments. En tant qu'argument, il accepte le nom de la propriété à partir des données d'entrée et peut gérer une propriété imbriquée (imbrication marquée par un point: property.nested);

  • Passer l'argument zéro renvoie la longueur des données d'entrée.
  • Passer un seul argument renvoie la somme simple des valeurs de ces propriétés.
  • Passer plus d'arguments retourne la somme des produits des valeurs des propriétés passées (somme scalaire des propriétés).

voici JS Fiddle et le 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

9
Vaclav Novotny

Solution simple

Voici une solution simple. Pas de boucle supplémentaire requise.

partie HTML

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

partie de 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;
 }
4
rgb

Une autre façon de résoudre ce problème, en partant de la réponse de Vaclav , pour résoudre ce calcul particulier - c’est-à-dire un calcul sur chaque ligne.

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

Pour ce faire avec un calcul, ajoutez simplement une fonction de calcul à votre étendue, par exemple.

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

Vous utiliseriez {{ datas|total:calcItemTotal|currency }} dans votre code HTML. Cela présente l'avantage de ne pas être appelé à chaque fois, car il utilise des filtres et peut être utilisé pour des totaux simples ou complexes.

JSFiddle

3
Marc Durdin

C'est un moyen simple de faire cela avec ng-repeat et ng-init pour agréger toutes les valeurs et étendre le modèle avec une propriété item.total.

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

La directive ngInit appelle la fonction set total pour chaque élément. La fonction setTotals dans le contrôleur calcule le total de chaque élément. Il utilise également les variables d’étendue factureCount et factureTotal pour agréger (somme) la quantité et le total de tous les articles.

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

pour plus d'informations et une démo, consultez ce lien:

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

3
ozkary

Vous pouvez utiliser un filtre personnalisé Angular prenant le tableau d'objets de l'ensemble de données et la clé de chaque objet. Le filtre peut alors renvoyer la somme:

.filter('sumColumn', function(){
        return function(dataSet, columnToSum){
            let sum = 0;

            for(let i = 0; i < dataSet.length; i++){
                sum += parseFloat(dataSet[i][columnToSum]) || 0;
            }

            return sum;
        };
    })

Ensuite, dans votre table pour additionner une colonne, vous pouvez utiliser:

<th>{{ dataSet | sumColumn: 'keyInObjectToSum' }}</th>
2
Snake

Je préfère les solutions élégantes

dans le modèle

<td>Total: {{ totalSum }}</td>

dans le contrôleur

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

Si vous utilisez ES2015 (alias ES6)

$scope.totalSum = Object.keys(cart.products)
  .map(k => +cart.products[k].price)
  .reduce((a, b) => a + b);
2
borodatych

voici ma solution à ce problème:

<td>Total: {{ calculateTotal() }}</td>

scénario

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

la réduction sera exécutée pour chaque produit dans la gamme de produits. L'accumulateur est le montant total accumulé, currentValue est l'élément actuel du tableau et le 0 dans le dernier est la valeur initiale

1

Vous pouvez essayer d’utiliser les services de angular js.

Code du contrôleur:

$scope.total = 0;
var aCart = new CartService();

$scope.addItemToCart = function (product) {
    aCart.addCartTotal(product.Price);
};

$scope.showCart = function () {    
    $scope.total = aCart.getCartTotal();
};

Code de service:

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

La réponse de Huy Nguyen est presque là. Pour que cela fonctionne, ajoutez:

ng-repeat="_ in [ products ]"

... à la ligne avec ng-init. La liste a toujours un seul élément, donc Angular répète le bloc exactement une fois.

La démo de Zybnek utilisant le filtrage peut être utilisée en ajoutant:

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

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

0
Aaron Queenan
**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;
        }
0

En html

<b class="text-primary">Total Amount: ${{ data.allTicketsTotalPrice() }}</b>

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);
            };
        });
0
Shaik Matheen

Prenant la réponse de Vaclav et la rendant plus anguleuse:

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

Cela vous donne l’avantage d’avoir même accès à des données imbriquées et matricielles:

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

J'ai développé un peu la réponse de RajaShilpa. Vous pouvez utiliser une syntaxe comme:

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

afin que vous puissiez accéder à l'objet enfant d'un objet. Voici le code pour le filtre:

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