How to calculate the sum of repeated elements in AngularJS? - angularjs

I am new to AngularJS and stuck on a problem from last one week.
I am getting dynamic weekly values for different groups.
My target is to caculate the sum of values for each group which is doable through filter, but I am not able to figure out how to re-calulate the weeklyvalues if I change the value of sum. What I have done so far:
Controller:
app.controller("CalculationController", [
"$scope", "calculationService", function ($scope, calculationService) {
getGroups();
function getGroups() {
calculationService.GetGroups().success(function(groups) {
$scope.groups= groups;
console.log(groups);
}).error(function (error) {
$scope.status = "Error Occured: " + error.message;
console.log($scope.status);
});
}$scope.getTotal = function () {
var groupTotal = [];
var total = 0; // hold the total for week
var totalPerGroup = 0;
var totalPerGroupArr = [[], []]; //array to store total values Per Group
var groups = $scope.groups; //getting the data of groups in a variable.
var totalCostForEachWeekPerMainGroup = [[], []]; //array to store total groups for each week
var totalGroupsCostPerGroup = []; //array to store the total cost per group
totalCostPerMainGroup = 0; //sum of all groups per Main Group
for (var i = 0; i < trps.length; i++) {
total = 0;
totalPerGroup = 0;
for (var j = 0; j < groups[i].WeeklyValues.length; j++) {
totalCostForEachWeekPerMainGroup[j][i] = groups[j].WeeklyValues[i].Cost;
totalPerGroupArr[i][j] = groups[i].WeeklyValues[j].Cost;
totalPerGroup += totalPerGroupArr[i][j];
total += totalCostForEachWeekPerMainGroup[j][i];
totalCostPerMainGroup += totalCostForEachWeekPerMainGroup[j][i];
}
groupTotal.push(total);
totalGroupsCostPerGroup.push(totalPerGroup);
$scope.totalGroupsCostPerMainGroup = totalCostPerMainGroup;
}
//Percentage calculation for each week
return groupTotal;
}
}]).filter('sumCalculation', function () {
return function (data, key) {
if (typeof (data) === 'undefined' || typeof (key) === 'undefined') {
return 0;
}
var total = 0;
for (var i = 0; i < data.length; i++) {
total += parseFloat(data[i][key]);
}
//$scope.sum = total;
return total;
}});
View:
<table class="table table-hover">
<tr>
<th>#Html.Label("Main Group")</th>
<th>#Html.Label("Group")</th>
<th ng-repeat="w in numberOfWeeks" ng-cloak>W{{w}}-2016</th>
<th>#Html.Label("Total")</th>
</tr>
<tr ng-repeat="(k, v) in GroupTotalPerMainGroupPerWeek"> // coming from another service
<td>{{k}}</td>
<td></td>
<td></td>
<td ng-repeat="cost in getTotal()">
{{cost}}
</td>
<td>{{totalGroupsCostPerMainGroup}}</td>
</tr>
<tr ng-repeat="g in groups">
<td></td>
<td ng-cloak>{{g.GroupName}}</td>
<td ng-repeat="c in g.WeeklyValues">
<input value="{{c.Cost}}" class="form-control" type="number" ng-model="c.Cost" ng-cloak/>
</td>
<td>
<input value="{{g.WeeklyValues|sumCalculation:'Cost'}}" class="form-control" string-to-number ng-model="Cost" type="number" ng-cloak/>
</td>
</tr>
</table>
Service:
app.factory("CalculationService", ['$http', function ($http) {
var CalculationService= {};
CalculationService.GetGroups = function () {
return $http.get("/Angular/GetGroups");
}
return CalculationService;}]);
What I want to achieve:
Calculate the sum of each value per group
Calculate the sum of all groups per week
Calculate the total sum of all groups
If i change the value of sum it should change the each weekly value accordingly
If i change the value of total (i.e. 1265 in example) it should recalculate the sum (value) of each group and each weekly value should be re-calculated:
The example of view is like:
GroupName------Week1Value------Week2Value------Week3Value------Sum
------------------ 312 ------------- 328 --------- 625----------- 1265
G1-------------- 112 ------------- 113 --------- 300 ---------- 525
G2-------------- 200 ------------- 215 --------- 325 ---------- 740

Related

Get filtered value from view to controller

I use ng-repeat to show my array of objects. One of the attributes is video duration, and I used filter to directly show sum of all meta_durations. here is filter
app.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 = dataDuration[key[j]];
data = data.meta_duration;;
return data;
}
}
})
and in view
<table>
<thead>
<tr>
<th>Media name</th>
<th>Media type</th>
<th>Media duration in sec</th>
<th>Media thumbnail</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="media in myMediaItems">
<td>{{media.name}}</td>
<td>{{media.type}}</td>
<td>>{{media.meta_duration}}</td>
<td><img src="{{media.thumbnail}}" alt="" /></td>
</tr>
</tbody>
<tr>
<td></td>
<td></td>
<td>Total duration {{ myMediaItems|sumProduct:'duration' }}</td> //here is where i use custom filter
<td></td>
</tr>
</table>
How to get total duration value in the controller?
Dynamically i add (push) media to list from another list with all media.
$scope.addToMediaList = function(item) {
var seconds = parseInt(item.meta_duration);
$scope.view_duration = Math.floor(moment.duration(seconds,'seconds').asHours()) + ':' + moment.duration(seconds,'seconds').minutes() + ':' + moment.duration(seconds,'seconds').seconds();($scope.view_duration_seconds, "HH:mm:ss");
$scope.myMediaItems.push(item);
$scope.sumDuration = $filter("sumProduct")(myMediaItems.meta_duration); //i try like this, set on $scope.sumDuration but in conole i get only number 0
$scope.myMediaItemsId.push(item.id);
$scope.input = "";
};
Thanks
I simplified your filter.
try this
app.filter('sumProduct', function() {
return function (input) {
var i = input instanceof Array ? input.length : 0;
var a = arguments.length;
if (i === 0)
return i;
var total = 0;
for(var x of input){
var duration = parseFloat(x.meta_duration);
if(isNaN(duration)){
throw 'filter sumProduct can count only numeric values';
}
total += duration;
}
return total;
}
});
You can use something like this
<div ng-init="sumDuration = myMediaItems|sumProduct:'duration'"> </div>
I solved this by forwarding the returned value from the custom filter to controller.
var filtered = $filter('sumProduct')($scope.myMediaItems);
console.log(filtered)
Filtered value is sum. thnx # Amaya San

angularjs custom sum filter doesn't work

im trying to do sum operation on my table Like
Item Price
abc 10
xyz 20
Ites=>2 Price=>30
For this i wrote simple custome filter
.filter('SumOfAmt', 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]);
Html code
<form name="form1" ng-submit="SaveDb(form1.$valid)" novalidate>
<b>cost</b><input type="number" ng-model="cost" required />
</form>
This is my Table when user enter in form it display in table
<table class="table table-bordered">
<tr>
<td>Name</td>
<td>Price</td>
</tr>
<tr ng-repeat="Emp in EmployeeList">
<td>{{Emp.EmpName}}</td>
<td>{{Emp.Cost | currency}}</td>
</tr>
This is {{EmployeeList|SumOfAmt:'Cost'}}
</table>
And what's wrong?
With your custom filter you can get the right sum, you just need to return sum:
.filter('SumOfAmt', 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]);
});
return sum;
};
});
But another alternative is:
.filter('SumOfAmt', function () {
return function (data, key) {
if (angular.isUndefined(data) || angular.isUndefined(key))
return 0;
return data.map(function(a){return parseInt(a[key])}).reduce((a, b) => a + b, 0);
};
});

The ng-repeat array is updating table data for the first time i'm selecting value, but its not updating the table data only once

I'm trying to update the table view depending on select option. The table view is updating only once, when i select the option second time the view is not updating, I'm not getting what's the problem. please help me solve this..
here is app.js
$scope.User = {};
$scope.arr = [];
$scope.loaddata = function(User) {
$scope.User.site = layouts;
AllServices.teamAllDataFunction1(User)
.then(function(response) {
$scope.User.data=response.data;
});
};
$scope.getdatalayoutwise = function(User) {
var total = 0;
var total1 = 0;
for (var i = 0; i < ($scope.User.data).length; i++) {
if($scope.User.data[i].Layout == $scope.User.selectedSite) {
total += parseInt($scope.User.data[i].dp_inst_pending);
$scope.arr.push($scope.User.data[i]);
}
}
for (var j = 0; j < ($scope.User.data1).length; j++) {
if($scope.User.data1[j].Layout == $scope.User.selectedSite) {
total1 += parseInt($scope.User.data1[j].DP_Inst_Pending);
}
}
$scope.User.teamTotal = total;
$scope.User.personalTotal = total1;
$scope.data = [$scope.User.teamTotal, $scope.User.personalTotal];
$scope.totamnt = parseInt($scope.User.personalTotal) + parseInt($scope.User.teamTotal);
$scope.User.totalamount = $filter('translate')('totalpending') + ": " + $filter('currency')($scope.totamnt, "");
$scope.User.data = $scope.arr;
};
here is html
<select name="site" ng-model="User.selectedSite" ng-change="getdatalayoutwise(User)">
<option value="">--{{'selectsite_message' | translate}}--</option>
<option ng-repeat= "option in User.site" value="{{option.Layout}}">{{option.Layout}}</option>
</select>
<table ng-table>
<tr>
<th>advisor_name</th>
<th>totalpending</th>
</tr>
<tr ng-repeat="data in User.data | filter : {Layout: User.selectedSite}: true" ng-if="data.dp_inst_pending">
<td class="ui-helper-center"><a ng-click="advisorDetails($index, data, User)">{{data.AdvisorName}}</a></td>
<td>{{data.dp_inst_pending | currency:"₹":0}}</td>
</tr>
</table>
you need to use $scope.$apply() :
$scope.getdatalayoutwise = function(User) {
$scope.$apply(function () {
var total = 0;
var total1 = 0;
for (var i = 0; i < ($scope.User.data).length; i++) {
if($scope.User.data[i].Layout == $scope.User.selectedSite) {
total += parseInt($scope.User.data[i].dp_inst_pending);
$scope.arr.push($scope.User.data[i]);
}
}
...
});
}
https://www.grafikart.fr/formations/angularjs/apply-watch-digest
Change your function to this
$scope.loaddata = function(User) {
$scope.User.data = [];
$scope.User.site = layouts;
AllServices.teamAllDataFunction1(User)
.then(function(response) {
$scope.User.data=response.data;
});
and add a ng-if
<table ng-table ng-if="User.data.length">
<tr>
<th>advisor_name</th>
<th>totalpending</th>
</tr>
<tr ng-repeat="data in User.data | filter : {Layout: User.selectedSite}: true" ng-if="data.dp_inst_pending">
<td class="ui-helper-center"><a ng-click="advisorDetails($index, data, User)">{{data.AdvisorName}}</a></td>
<td>{{data.dp_inst_pending | currency:"₹":0}}</td>
</tr>
</table>
Add this as the first line in getdatalayoutwise () function:
$scope.arr = [];
got it working by just doing following
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
$scope.getdatalayoutwise = function(User) {
var total = 0;
var total1 = 0;
for (var i = 0; i < ($scope.User.data).length; i++) {
if($scope.User.data[i].Layout == $scope.User.selectedSite) {
total += parseInt($scope.User.data[i].dp_inst_pending);
$scope.arr.push($scope.User.data[i]);
}
}
...
$scope.safeApply (function () {
$scope.User.data = $scope.arr;
});
};

Calculate the Sum of Values using ng-repeat in Angular js

I am newbie to Angular js.I am just trying to calculate the sum of Values of each row using angular js.Here, I am dynamically adding and removing rows.
For Eg: Let's say first row value is 30, Now I added one row with value 30, then total should be show 60 like this.
1. 30
2. 30
3. 50
Total 120
HTML Code:
<table id="t1" style="border:none;">
<tr><th>Start</th><th>Stop</th><th>Downtime(In Min)</th><th>Reason</th></tr>
<tr ng-repeat="item in invoice">
<td><input type="text" required ng-model="$invoice.start" name="r1[]"></td>
<td><input type="text" required ng-model="$invoice.stop" name="r2[]" ng-blur="diff($invoice)"></td>
<td><input type="text" name="r3[]" ng-model="$invoice.diff"/></td>
<td style="border:none;"><a href ng-click="remove(item)">X</a></td>
</tr>
<tr style="border:none;">
<td style="border:none;"><a href ng-click="add()">+</a></td>
</tr>
</table>
<span class="labelCode">Total Downtime</span><input required type="text" name="Tot_D" ng-value="d1()"/></span></br>
Angular JS:
$scope.d1=function(){
var total = 0;
for(var i = 0; i < $scope.invoice.length; i++){
var product = $scope.invoice[i];
total += parseInt(product.diff);
}
return total;
}
//Calculate the Difference between two times
$scope.diff = function(item) {
item.diff = computeDiff(item.start,item.stop);
}
function computeDiff(start, stop) {
if (start && stop) {
var s_hr = start.split(":")[0];
var s_min = start.split(":")[1];
var e_hr = stop.split(":")[0];
var e_min = stop.split(":")[1];
return Math.abs((parseInt(e_hr) - parseInt(s_hr)) * 60) + Math.abs(parseInt(e_min) - parseInt(s_min))
}
}
Now, it just showing NaN. I don't know what is wrong.
Please help out.Thanks in advance.

Calculate in ng-repeat from the entry before

I have a filtered list (which is filtered by time - so in a specific timeframe) and over these item I am iterating with ng-repeat. These items have a name and a price. So if I am iterating over them I want to achieve that I always show the "sub"-total like this:
DATE NAME PRICE SUBTOTAL
2014-05 T-Shirt 20.00 20.00
2014-05 Jeans 45.00 65.00
2014-05 Cap 15.00 80.00
These Items are sorted by date but might have a different ID (ids dont match the index!).
I am really not able to find out how I could always calculate the subtotal (the table can be filtered by date ranges, means I could also include the items from 2014-04 and it should recalculate dynamically.
I tried it with a function like this in the controller:
var curBalanceCounter2 = 0;
$scope.currentBalanceCalc = function(finance) {
curBalanceCounter2 = curBalanceCounter2 + finance.amount;
return curBalanceCounter2;
}
But this i being executed 10 times so I get wrong numbers. Any better solution?
Thank you.
Create a custom filter
myApp.filter('subtotal', function(){
return function(items, index){
var subtotal = 0;
for (var i = 0; i <= index; i++) {
subtotal += items[i].price
}
return subtotal || items[index].price;
}
});
and call it like so
<li ng-repeat="item in items">{{item.name}} - {{item.price}} -
{{ items | subtotal : $index}}</li>
Demo
Since you have access to the original list (e.g. items in the code above) inside of an ng-repeat, you can pass it, along with the index of the current item, into a custom filter. This filter can then loop through each item up to and including the index passed in, and then return a summed subtotal. If the subtotal is 0 (as it would be for a first item), instead return the price of that item.
Docs: Custom filters in Angular
This is similar to Marc's answer. Define a subtotal function in the controller:
$scope.subtotal = function(index){
var total = 0;
angular.forEach($scope.data, function(value, key){
if(key <= index)
total += value.Price;
});
return total;
}
Then use it like this in the view:
<tr ng-repeat="d in data">
<td>{{d.Date}}</td>
<td>{{d.Name}}</td>
<td>{{d.Price}}</td>
<td>{{subtotal($index)}}</td>
</tr>
Demo
Update
If the issue is that the data isn't already sorted on the client, but is being sorted by a filter on the ng-repeat, then here's the fix:
Pass in the orderBy parameter to the subtotal function, and execute the filter on the data before computing the subtotals:
$scope.orderBy = 'Date';
$scope.subtotal = function(index, orderBy){
var total = 0;
angular.forEach($filter('orderBy')($scope.data,orderBy), function(value, key){
if(key <= index)
total += value.Price;
});
return total;
}
I've updated my demo with this code. You can change the sort order by changing 'Date' to 'Name' or 'Price' on this line
$scope.orderBy = 'Date';
and see that the subtotals automatically recalculate.
I don't know of a way to do this in pure angular, perhaps someone will chime in.
What you need looks like a cumulative sum:
function cSum(arr) {
var cumsum = [];
for(var i=0;i<arr.length;i++) {
if(i==0) cumsum[i] = arr[0];
else cumsum[i] = cumsum[i-1] + arr[i];
}
return cumsum
}
Then just add that field into the array of objects that you are repeating over and you can display it in the table.
Not too hard to do http://jsfiddle.net/VAJ5S/3/
HTML
<div ng-app="myApp">
<table ng-controller="myController">
<thead>
<tr>
<th>DATE</th>
<th>NAME</th>
<th>PRICE</th>
<th>SUBTOTAL</th>
</tr>
</thead>
<tr ng-repeat="item in items">
<td>{{item.date}}</td>
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>{{subtotal($index)}}</td>
</tr>
</table>
</div>
JS
var app = angular.module("myApp", []);
app.controller("myController", ["$scope", function($scope){
$scope.items = [
{
date: "2014-05",
name: "T-Shirt",
price: 20.00
},
{
date: "2014-05",
name: "Jeans",
price: 65.00
},
{
date: "2014-05",
name: "Cap",
price: 80.00
}
];
$scope.subtotal = function(ind){
var subtotal = 0;
for (var i = 0; i<=ind; i++){
subtotal += $scope.items[i].price;
}
return subtotal;
};
}]);

Resources