0

I'm facing a need to display a sum of values (related to $scope variables) depending on the selection of flags. For instance:

  • There are 4 $scope variables (e.g. $scope.Var_1, $scope.Var_2...) containing integer values,
  • There are 4 $scope variables (e.g. $scope.Var_1_Flag, $scope.Var_2_Flag...)containing true or false for each of the above integer variables.

So, in we have:

$scope.Var_1 = 1 ;
$scope.Var_2 = 2 ;
$scope.Var_3 = 3 ;
$scope.Var_4 = 4 ;

$scope.Var_1_Flag = true ;
$scope.Var_2_Flag = true ;
$scope.Var_3_Flag = true ;
$scope.Var_4_Flag = true ;

then 10 will be displayed, but if:

$scope.Var_1_Flag = true ;
$scope.Var_2_Flag = false;
$scope.Var_3_Flag = false;
$scope.Var_4_Flag = true ;

then 5 will be displayed.

Does AngularJS support a binding syntax that would realize this?

halfer
  • 19,824
  • 17
  • 99
  • 186
FDavidov
  • 3,505
  • 6
  • 23
  • 59
  • 1
    Can't you create a set of objects? Each object can have the var value and the flag. Then we can use JS built in functions to get the sum with a less effort. I am not sure if this would match in your case, please let me know. Thanks – Kushan Randima Aug 03 '17 at 06:43
  • Technically yes, but that would be a bad idea since the results of these calculations are used to populate a table with dynamic size. – FDavidov Aug 03 '17 at 07:49

8 Answers8

3

MARKUP:

<div ng-controller="MyCtrl">
  <input type="checkbox" ng-model="Var_1_Flag" ng-checked="Var_1_Flag" ng-change="changeStatus(Var_1_Flag);" />
  <input type="checkbox" ng-model="Var_2_Flag" ng-checked="Var_2_Flag" ng-change="changeStatus(Var_2_Flag);" />
  <input type="checkbox" ng-model="Var_3_Flag" ng-checked="Var_3_Flag" ng-change="changeStatus(Var_3_Flag);" />
  <input type="checkbox" ng-model="Var_4_Flag" ng-checked="Var_4_Flag" ng-change="changeStatus(Var_4_Flag);" />  
  <br/> Sum is: {{sum}}
</div>

JS:

var myApp = angular.module('myApp', []);

function MyCtrl($scope) {
  $scope.sum = 0;
  $scope.Var_1 = 1;
  $scope.Var_2 = 2;
  $scope.Var_3 = 3;
  $scope.Var_4 = 4;

  $scope.Var_1_Flag = true;
  $scope.Var_2_Flag = false;
  $scope.Var_3_Flag = false;
  $scope.Var_4_Flag = true;

  $scope.changeStatus = function(checkValue) {
    $scope.checkValue = !checkValue;
    $scope.calculateSum();
  }

  $scope.calculateSum = function() {
    $scope.sum = ($scope.Var_1_Flag ? $scope.Var_1 : 0) + ($scope.Var_2_Flag ? $scope.Var_2 : 0) + ($scope.Var_3_Flag ? $scope.Var_3 : 0) + ($scope.Var_4_Flag ? $scope.Var_4 : 0)
  }
  $scope.calculateSum();
}

Check this http://jsfiddle.net/ananyaojha/ADukg/13641/

  // Need to keep track of watcher
  $scope.$watch('Var_1_Flag', function(newVal, oldVal){
     // this callback is invoked if any change is detected in the value of Var_1_Flag

    // add condition and update scope using $apply or $evalAsync
    // You have to set watchers also whenever flags are keep getting changed for all falg types.
  })
  • This can be a way –  Aug 03 '17 at 06:43
  • Thank you slaker for your suggestion. The problem is that the user can turn the flags on and off (checkboxes) and the results need to be updated. The second part of the problem is that these **calculated** values fill a relatively large table. I know that within HTML tags some calculations can take place, but I'm not sure about the syntax. – FDavidov Aug 03 '17 at 06:52
  • We can create a sum function which can be called on change of checkbox which would give a proper result –  Aug 03 '17 at 06:56
  • Yeah I mean whoever downvoted, they should explain in comment. Then only we can improve our answer –  Aug 03 '17 at 07:23
  • Slaker, please see the answer I posted. It appears to be far simpler and it does provide the needed result. I will, however, up-vote it. Thanks for your efforts. – FDavidov Aug 03 '17 at 10:43
  • Thank you. "ll have a look for sure. –  Aug 03 '17 at 10:47
1

you will have to watch the scope variables

$scope.$watch('Var_1_Flag', function(newVal, oldVal){
   // this callback is invoked if any change is detected in the value of Var_1_Flag

  // add condition and update scope using $apply or $evalAsync
})

you could set up more watchers or add all the flag variables into a object and then watch the object so you don't have to use a different callback for each scope variable

Syed Faizan
  • 901
  • 3
  • 14
  • 28
  • Hello Syed. Pleas take a look at the solution I posted (marked as the answer). I works beautifully without `$watch` at all. – FDavidov Aug 09 '17 at 04:54
0

Create an Array of Objects with value and flag propeties. And create filter to check the flag and sum of only those values.

$scope.sumArray = [
    {value:1,flag:true},
    {value:2,flag:false},
    {value:3,flag:false},
    {value:4,flag:true}
];
pranav-dev
  • 68
  • 1
  • Could you add an example of how the filter would look and how you would have this set within the page? Note that the data is to be used to populate a potentially large table. Each cell will contain 4 numbers (different sets for different cells) while the 4 flags are global (i.e. same flags setting for all the cells). – FDavidov Aug 03 '17 at 07:02
  • You have not added the exact requirement, where it needs to be applicable. But the code is added by @korcan below. to add only those values which is having true flag.. rest will be filtered. – pranav-dev Aug 03 '17 at 07:38
0

You could instead assign the function the the $scope.variable..makes it more easier..hope this is what you are looking for

angular.module('myApp', [])
  .controller('MainCtrl', function($scope) {
    $scope.Var_1_Flag = true;
    $scope.Var_2_Flag = false;
    $scope.Var_3_Flag = true;
    $scope.Var_4_Flag = true;

    $scope.var_1 = function() {
      if ($scope.Var_1_Flag) {
        return 1;
      } else {
        return 0;
      }
    }
     $scope.var_2 = function() {
      if ($scope.Var_2_Flag) {
        return 2;
      } else {
        return 0;
      }
    }


  });
<body ng-app="myApp" ng-controller="MainCtrl">
  <div>
    <span>{{var_1() + var_2()}} </span> 
  </div>
</body>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
  • Thank you Sushil for this quite interesting approach. The issue with it is that this type of calculation would need to be used to populate a rather large table and dynamic table; hence, I doubt it would be practical to go this way (though I would embrace it in other situations). – FDavidov Aug 03 '17 at 06:57
  • @FDavidov i agree :) .. this is rather for a small data – Sushil Bastola Aug 04 '17 at 12:44
0

In your controller :

$scope.sumValues =0;
var Values =[
  {v:1,f:true},
  {v:2,f:false},
  {v:3,f:false},
  {v:4,f:true}];
Values.forEach(function(element) {    
   if(element.f)
        $scope.sumValues += element.v;
});

and in your HTML :

<div ng-controller="MyCtrl"> 
   {{sumValues}}
</div>

I create an example for you :

http://jsfiddle.net/ADukg/13643/

Emad Dehnavi
  • 3,262
  • 4
  • 19
  • 44
0
$scope.sumArray = [
    {value:1,flag:true},
    {value:2,flag:false},
    {value:3,flag:false},
    {value:4,flag:true}
];

function sum(){
  $scope.sum =0;
  for(var i=0;i<$scope.sumArray.length;i++){
     $scope.sum = $scope.sum + 
        $scope.sumArray[i].flag ? $scope.sumArray[i].value: 0 
  }
}

$scope.$watch('$scope.sumArray', sum,true);

or :

you can use $filter

function sum(){
  $scope.sum=0;
  var filtered = $filter('filter')($scope.sumArray,'flag');
  for(var i=0;i<filtered.length;i++){
     $scope.sum = $scope.sum+filtered[i].value;
  }
}
Mumin Korcan
  • 101
  • 7
  • By the way, you can change flags on the view with checkboxes and this solution will work for arrays whose lengths are bigger than 4. – Mumin Korcan Aug 03 '17 at 07:46
0

You just need One $watch to update the values of sum. Watch all the flags together and whenever the checkbox(flag) changes, the sum will automatically update.

var myApp = angular.module('myApp', []);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
myApp.controller("MyCtrl", function($scope) {
    $scope.sum = 0;
    $scope.Var_1 = 1;
    $scope.Var_2 = 2;
    $scope.Var_3 = 3;
    $scope.Var_4 = 4;
    $scope.Var_1_Flag = true;
    $scope.Var_2_Flag = false;
    $scope.Var_3_Flag = false;
    $scope.Var_4_Flag = true;
    $scope.$watch('Var_1_Flag + Var_2_Flag + Var_3_Flag +Var_4_Flag', function(val) {
        $scope.sum = ($scope.Var_1_Flag ? $scope.Var_1 : 0) + ($scope.Var_2_Flag ? $scope.Var_2 :
            0) + ($scope.Var_3_Flag ? $scope.Var_3 : 0) + ($scope.Var_4_Flag ? $scope.Var_4 :
            0);
    })
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
    <h6>
CheckBoxes
</h6>
    <input type="checkbox" ng-model="Var_1_Flag">
    <input type="checkbox" ng-model="Var_2_Flag">
    <input type="checkbox" ng-model="Var_3_Flag">
    <input type="checkbox" ng-model="Var_4_Flag">
    <h6>
Sum
</h6> {{sum}}
</div>
Vivz
  • 6,625
  • 2
  • 17
  • 33
0

I found an amazingly simple solution that does the job exactly as I wanted.

Here is a piece of code within the controller:

$scope.Test_1_Value = 1     ;
$scope.Test_1_Flag  = true  ;

$scope.Test_2_Value = 2     ;
$scope.Test_2_Flag  = true  ;

$scope.Test_3_Value = 3     ;
$scope.Test_3_Flag  = true  ;

$scope.Test_4_Value = 4     ;
$scope.Test_4_Flag  = true  ;


$scope.ConditionalAdd = function (p1,p2,p3,p4) {
    var aaa = 0 ;
    if ($scope.Test_1_Flag) {aaa = aaa + $scope.Test_1_Value }
    if ($scope.Test_2_Flag) {aaa = aaa + $scope.Test_2_Value }
    if ($scope.Test_3_Flag) {aaa = aaa + $scope.Test_3_Value }
    if ($scope.Test_4_Flag) {aaa = aaa + $scope.Test_4_Value }
    return aaa ;
}

and here the HTML part:

    <input type="checkbox" ng-model="Test_1_Flag"> Add 1
    <br>
    <input type="checkbox" ng-model="Test_2_Flag"> Add 2
    <br>
    <input type="checkbox" ng-model="Test_3_Flag"> Add 3
    <br>
    <input type="checkbox" ng-model="Test_4_Flag"> Add 4
    <br>
    <label>Total 1: </label> {{ConditionalAdd(Test_1_Value,Test_2_Value,Test_3_Value,Test_4_Value)}}

As the checkboxes are changed (checked/unchecked), the result shown next to Total 1: is updated automatically, as needed.

The values Test_x_Value are part of the data generated for the creation and population of the table (using ng-repeat), and hence are available within each single cell of the table.

So, no filters, no watches.

Thanks to every one for your support :-).

EDIT:

I just finished implementing this solution and tested it with a table containing over 2,500 cells. This solution works perfectly well, including performance.

FDavidov
  • 3,505
  • 6
  • 23
  • 59
  • This answer might solve the problem but the performance will be bad. It is not recommended to call function from view https://stackoverflow.com/questions/29474445/why-does-angularjs-execute-function-on-every-digest-loop . – Vivz Aug 03 '17 at 10:10
  • I'll see how (and if) performance is affected in a noticeable way and, if so, will return to this question / answer and update it with the observations. Another thing I can try is to set it as a one-way binding (i.e. with the `:` character) since data is always from the logic to the HTML but not the other way. We'll see. – FDavidov Aug 03 '17 at 10:21
  • @Vivz, I checked your point (poor performance) and I cannot see any issue at all. See the **EDIT**: table with over 2,500 cells, most of them including calculations, and the response time is much the same (i.e. I tested updating with and without the calculation - commented out - and was not able to detect any change in performance). – FDavidov Aug 09 '17 at 04:58