ng-click to execute after ng-model changes

1.9k views Asked by At

I was wondering if there was a way I could have a ng-click function which takes in a scope variable execute after the value of the ng-model of that same scope variable changes. I have a list of analysis objects in a list called analyses that have (among others) an include boolean attribute. They are displayed in html as button checkboxes to press to include/exclude.

I have this html:

Number of bisulfite sequences: {{included}} used / {{excluded}} excluded
<div ng-repeat="analysis in analyses"><br><label class="btn btn-success" 
ng-model="analysis.include" ng-click="set(analysis.include)" btn-checkbox>

This is the code in the controller:

$scope.analyses = some_list_of_analyses
$scope.included = 0
$scope.excluded = $scope.analyses.length

$scope.set = function(include){

//more code to execute involving $scope.analyses

  if(include){
    $scope.included += 1
    $scope.excluded -= 1
  }
  else{
    $scope.excluded += 1
    $scope.included -= 1
  }
}

right now, assuming I start with a list of analyses that are all excluded as you can see in the initialization, when I click on a button, it should increase the included scope variable by one and decrease the excluded by one. instead, it passes in analysis.include before the variable actually changes so the first button I click shows -1 used / n+1 excluded.

The solutions I can think of are either to find some way of having ng-click execute after the scope variable is bound after ng-model, or some way to change $scope.analyses objects within the ng-click method, so then the ng-model statement isn't needed?

Thanks!

EDIT: the ng-model statement is actually needed for the btn-checkbox directive. I need this directive to show a toggle on/off for including/excluding certain analyses. Alternative directives or code to implement css for indicating inclusion/exclusion would be helpful!

4

There are 4 answers

4
jsuser On BEST ANSWER

What if instead of passing in the analysis.included you get it from the scope in your function

I created a plunkr to demonstrate

basically HTML:

 Number of bisulfite sequences: {{included}} used / {{excluded}} excluded

<div ng-repeat="analysis in analyses">
  <br />
  <label class="btn btn-success" ng-model="analysis.include"  btn-checkbox>
    <button ng-click="set($index)">Click Me!</button>
  </label>
</div>

So here, you pass in the $index so you know which one to look at on $scope in the controller. so then in your controller:

$scope.set = function(i){

 var include = $scope.analyses[i].include;
 //more code to execute involving $scope.analyses

 if(include){
  $scope.included += 1
  $scope.excluded -= 1
 }
 else{
  $scope.excluded += 1
  $scope.included -= 1
 }
};

It seemed to work in the plunkr. the included went up and the excluded went down

Update: New Plunkr that toggles included/excluded instead of only incrementing.

As well as updated set function:

$scope.set = function(i){

 $scope.analyses[i].include = !$scope.analyses[i].include;

 include = $scope.analyses[i].include

 //more code to execute involving $scope.analyses

if(include){
  $scope.included += 1
  $scope.excluded -= 1
 } else {
  $scope.excluded += 1
  $scope.included -= 1
 }
};
2
Andy Gaskell On

I'm not quite sure I follow, but I think we can simplify things.

First, let's say that each analysis has a property called included.

This allows you to write a couple methods like this:

$scope.included = function() {
  var count = 0;
  for(var i = 0; i < $scope.analyses.length; i++) {
    if($scope.analyses[i].included) {
      count++;
    }
  }
  return count;
};

$scope.excluded = function() {
  var count = 0;
  for(var i = 0; i < $scope.analyses.length; i++) {
    if(!$scope.analyses[i].included) {
      count++;
    }
  }
  return count;
};

I'll let you do the refactoring to keep it DRY. Then your UI can do something like this:

Number of bisulfite sequences: {{included()}} used / {{excluded()}} excluded
<div ng-repeat="analysis in analyses"><br>
<label class="btn btn-success" ng-model="analysis" ng-click="set(analysis)" btn-checkbox>

And your click can be very simple (note that we are now passing in the analysis object, you can remove the ng-model attribute):

$scope.set = function(analysis) {
  $scope.analysis.included = !$scope.analysis.included;
};
2
FrankieAvocado On

My suggestion is to alter the way you are tracking the included / excluded values.

If, instead of adding/subtracting from values you simply track the length of the array then everything should fall into place.

Here is what I'm talking about. Your HTML could look like this:

<div ng-controller="solutionController">
Number of bisulfite sequences: {{included.length}} used / {{analyses.length - included.length}} excluded
<div ng-repeat="analysis in analyses">
    <br />
    <label class="btn btn-success" ng-click="include(analysis)" btn-checkbox=""></label>
</div>

And your controller could look something like this:

angular.module("routerApp").controller("solutionController", ["$scope", function($scope){
    $scope.included = [];
    $scope.analyses = ["sodium", "sulfur", "calcium"];

    $scope.include = function(includeMe){
        $scope.included.push(includeMe);
    };

}]);

I mean I don't know exactly what your analyses objects look like etc, but this should get you up and running on binding those counts to the actual array lengths.

0
David Barker On

I'm not sure I follow but your conditional logic in $scope.set is looking for a true value before the include property is increased. This is clearly not happening as demonstrated in this plunker example of your code in action.

The only difference is that I have made sure that include is a property of the first three analysis && set to true. Your code is working as expected, your analysis objects are not.