【问题标题】:Calculating sum of repeated elements in AngularJS ng-repeat计算AngularJS ng-repeat中重复元素的总和
【发布时间】:2014-05-08 23:54:07
【问题描述】:

下面的脚本使用ng-repeat 显示购物车。对于数组中的每个元素,它会显示项目名称、数量和小计 (product.price * product.quantity)。

计算重复元素总价最简单的方法是什么?

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

【问题讨论】:

标签: angularjs angularjs-ng-repeat


【解决方案1】:

在模板中

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

在控制器中

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

【讨论】:

  • 这样做的一个缺点是它对集合进行了两次迭代。这对于小型收藏品来说很好,但如果收藏品相当大怎么办?似乎在 ng-repeat 中应该有一种方法可以对给定的对象字段进行运行总和。
  • @Pascamel 检查我的答案(stackoverflow.com/questions/22731145/…) 我认为那个可以满足您使用过滤器提出的问题
  • 正是我遇到这个问题时所寻找的,感谢@RajaShilpa 的提醒!
  • 这个解决方案的主要问题是每次摘要都会重新计算总数,因为它是一个函数调用。
  • @icfantv 如何对集合进行两次迭代?
【解决方案2】:

这也适用于过滤器和普通列表。首先要为列表中所有值的总和创建一个新过滤器,并为总数量的总和提供解决方案。 详细代码检查它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>

检查这个Fiddle Link

【讨论】:

  • 嘿,我在使用resultValue 时收到'undefined',但如果我使用items,它可以正常工作,有什么想法......??
  • 首先检查以下代码“resultValue=(items | filter:{'details':searchFilter})”,因为所有过滤器值都存储在该变量“resultValue”中。我认为您误认为 {} 或 () 这个,再次验证。
  • 如果我使用items,它将无法与过滤器一起使用,帮助!
  • 我的代码是这样的ng-repeat="campaign in filteredCampaigns=(campaigns | filter:{'name':q})"{{ filteredCampaigns | campaignTotal: 'totalCommission' | number: 2 }}
  • 是的,因为项目还没有被过滤,在过滤发生后,结果必须存储到任何其他模型,并且应该只使用那个模型。在我的示例中,我使用了“resultValue”模型。
【解决方案3】:

我对 RajaShilpa 的回答做了一些扩展。您可以使用如下语法:

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

以便您可以访问对象的子对象。这是过滤器的代码:

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

【讨论】:

    【解决方案4】:

    这是我的解决方案

    甜美简单的自定义过滤器:

    (但仅与简单的值总和有关,而不是总和产品,我已编造了sumProduct 过滤器并将其作为编辑附加到这篇文章中)。

    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

    编辑:sumProduct

    这是sumProduct 过滤器,它接受任意数量的参数。作为参数,它接受来自输入数据的属性名称,并且它可以处理嵌套属性(用点标记的嵌套:property.nested);

    • 传递零参数返回输入数据的长度。
    • 仅传递一个参数会返回该属性值的简单总和。
    • 传递更多参数会返回传递的属性值的乘积总和(属性的标量总和)。

    这里是 JS Fiddle 和代码

    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

    【讨论】:

      【解决方案5】:

      很久以前就意识到这个答案,但想发布未提出的不同方法...

      使用ng-init 计算您的总数。这样,您不必在 HTML 中进行迭代并在控制器中进行迭代。在这种情况下,我认为这是一个更清洁/更简单的解决方案。 (如果计数逻辑更复杂,我肯定会建议将逻辑移至适当的控制器或服务。)

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

      当然,在您的控制器中,只需定义/初始化您的 Total 字段:

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

      【讨论】:

      • 这绝对是最有棱角的方式。简单、易读和声明性。所以,它所代表的逻辑仍然在它所属的地方。
      • 此方法将计算隐藏在单元格表示中,这里很容易理解,但是对于复杂的表格会变得非常混乱。
      • 另一个问题是它也没有双向绑定。
      【解决方案6】:

      采用 Vaclav 的答案并使其更像 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;
              }
          };
      }]);
      

      这让您甚至可以访问嵌套和数组数据:

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

      【讨论】:

        【解决方案7】:

        这是一种使用 ng-repeat 和 ng-init 聚合所有值并使用 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>
        

        ngInit 指令为每个项目调用 set total 函数。 控制器中的 setTotals 函数计算每个项目的总数。它还使用 invoiceCount 和 invoiceTotal 范围变量来聚合(汇总)所有项目的数量和总数。

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

        有关更多信息和演示,请查看此链接:

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

        【讨论】:

        • 在 StackOverlow 上不鼓励链接到您的博客文章的链接可能会失效。此外,当我查看页面时,我在页面中间收到 502 Bad Gateway 错误。在此处回答问题,而不是指向其他地方的链接。
        【解决方案8】:

        解决此问题的另一种方法,从 Vaclav 的 answer 扩展以解决此特定计算 - 即对每一行进行计算。

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

        要通过计算执行此操作,只需将计算函数添加到您的范围,例如

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

        您可以在 HTML 代码中使用 {{ datas|total:calcItemTotal|currency }}。这样做的好处是不需要为每个摘要调用,因为它使用过滤器,并且可以用于简单或复杂的总计。

        JSFiddle

        【讨论】:

          【解决方案9】:

          我更喜欢优雅的解决方案

          在模板中

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

          在控制器中

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

          如果你使用的是 ES2015(又名 ES6)

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

          【讨论】:

            【解决方案10】:

            您可以在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>
            

            在这里查看结果:http://plnkr.co/edit/Gb8XiCf2RWiozFI3xWzp?p=preview

            如果自动更新结果:http://plnkr.co/edit/QSxYbgjDjkuSH2s5JBPf?p=preview(感谢 - VicJordan)

            【讨论】:

            • 这在过滤列表时不起作用 - tbody 仅初始化一次,但 tr 每次过滤列表时都会导致总和不正确
            • 你能举个关于 plnkr 或 jsfiddle 的例子吗?
            • 嗯,是的,它在过滤器中不起作用,因为这里的过滤器只是在视图中显示/隐藏,而不是更新$scope
            • @HuyNguyen,我已经编辑了你上面的代码。请在此处查看:plnkr.co/edit/QSxYbgjDjkuSH2s5JBPf?p=preview。这里我想要的是如果用户更改数量,那么第 4 列(价格 * 数量)应该会自动更新。请你看看这个。谢谢
            【解决方案11】:

            在阅读了这里的所有答案 - 如何汇总分组信息后,我决定跳过这一切,只加载一个 SQL javascript 库。我正在使用 alasql,是的,加载时间要长几秒钟,但在编码和调试中节省了无数时间,现在分组和 sum() 我只是使用,

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

            我知道这听起来有点像对 angular/js 的咆哮,但实际上 SQL 在 30 多年前就解决了这个问题,我们不应该在浏览器中重新发明它。

            【讨论】:

            • 这太糟糕了。哇 SMH - 我会让其他人投票失败。这个答案我的嘴巴张得大大的.....
            【解决方案12】:

            在html中

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

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

            【讨论】:

              【解决方案13】:

              简单的解决方案

              这是一个简单的解决方案。不需要额外的 for 循环。

              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>
              

              脚本部分

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

              【讨论】:

                【解决方案14】:

                Huy Nguyen 的答案几乎就在那里。要使其工作,请添加:

                ng-repeat="_ in [ products ]"
                

                ...到 ng-init 的那一行。该列表始​​终只有一个项目,因此 Angular 将恰好重复该块一次。

                Zybnek 使用过滤的演示可以通过添加:

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

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

                【讨论】:

                  【解决方案15】:

                  这是我对这个问题的解决方案:

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

                  脚本

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

                  reduce 将对 products 数组中的每个产品执行。 Accumulator 为累计总量,currentValue 为数组的当前元素,最后的 0 为初始值

                  【讨论】:

                    【解决方案16】:

                    您可以尝试使用 Angular js 的服务,它对我有用..给出下面的代码 sn-ps

                    控制器代码:

                    $scope.total = 0;
                    var aCart = new CartService();
                    
                    $scope.addItemToCart = function (product) {
                        aCart.addCartTotal(product.Price);
                    };
                    
                    $scope.showCart = function () {    
                        $scope.total = aCart.getCartTotal();
                    };
                    

                    服务代码:

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

                    【讨论】:

                      【解决方案17】:

                      您可以使用自定义 Angular 过滤器,该过滤器将数据集对象数组和每个对象中的键相加。然后过滤器可以返回总和:

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

                      然后在您的表格中汇总您可以使用的列:

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

                      【讨论】:

                        【解决方案18】:
                        **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;
                                }
                        

                        【讨论】:

                        • 点评来源: 欢迎来到 Stack Overflow!请不要只用源代码回答。尝试对您的解决方案如何工作提供一个很好的描述。请参阅:How do I write a good answer?。谢谢
                        【解决方案19】:

                        这是我的解决方案

                        <div ng-controller="MainCtrl as mc">
                          <ul>
                              <li ng-repeat="n in [1,2,3,4]" ng-init="mc.sum = ($first ? 0 : mc.sum) + n">{{n}}</li>
                              <li>sum : {{mc.sum}}</li>
                          </ul>
                        </div>
                        

                        它要求您将名称添加到控制器为Controller as SomeName,以便我们可以在其中缓存变量(真的需要吗?我不熟悉使用 $parent 所以我不知道)

                        然后对于每个重复,添加ng-init"SomeName.SumVariable = ($first ? 0 : SomeName.SumVariable) + repeatValue"

                        $first 用于首先检查它然后重置为零,否则它将继续汇总值

                        http://jsfiddle.net/thainayu/harcv74f/

                        【讨论】:

                          【解决方案20】:

                          我通常使用下面这个简单的代码。 确保 vm.myArray 列表在计算中不为 'null'。

                          vm.totalQuantity = 0;
                          $.each(vm.myArray, function (i, v) {
                               vm.totalQuantity += v.Quantity;
                          });
                          

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 2016-12-29
                            • 1970-01-01
                            • 1970-01-01
                            • 2018-07-29
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多