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