Clicking a checkbox with ng-click does not update the model

AngularjsCheckbox

Angularjs Problem Overview


Clicking on a checkbox and calling ng-click: the model is not updated before ng-click kicks in so the checkbox value is wrongly presented in the UI:

This works in AngularJS 1.0.7 and seems broken in Angualar 1.2-RCx.

<div ng-app="myApp" ng-controller="Ctrl">
<li  ng-repeat="todo in todos">
  <input type='checkbox' ng-click='onCompleteTodo(todo)' ng-model="todo.done">
    {{todo.text}}
</li> 
<hr>
task: {{todoText}}
<hr><h2>Wrong value</h2>
     done: {{doneAfterClick}}

and controller:

angular.module('myApp', [])
  .controller('Ctrl', ['$scope', function($scope) {
    $scope.todos=[
        {'text': "get milk",
         'done': true
         },
        {'text': "get milk2",
         'done': false
         }
        ];
        

   $scope.onCompleteTodo = function(todo) {
    console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
    $scope.doneAfterClick=todo.done;
    $scope.todoText = todo.text;

   };
}]);

Broken Fiddle w/ Angular 1.2 RCx

Working fidddle w/ Angular 1.0.0

Angularjs Solutions


Solution 1 - Angularjs

How about changing

<input type='checkbox' ng-click='onCompleteTodo(todo)' ng-model="todo.done">

to

<input type='checkbox' ng-change='onCompleteTodo(todo)' ng-model="todo.done">

From docs: > Evaluate given expression when user changes the input. The expression is not evaluated when the value change is coming from the model. > > Note, this directive requires ngModel to be present.

Solution 2 - Angularjs

As reported in https://github.com/angular/angular.js/issues/4765, switching from ng-click to ng-change seems to fix this (I am using Angular 1.2.14)

Solution 3 - Angularjs

The order in which ng-click and ng-model will be executed is ambiguous (since neither explicitly set their priority). The most stable solution to this would be to avoid using them on the same element.

Also, you probably do not want the behavior that the examples show; you want the checkbox to respond to clicks on the complete label text, not only the checkbox. Hence, the cleanest solution would be to wrap the input (with ng-model) inside a label (with ng-click):

<label ng-click="onCompleteTodo(todo)">
  <input type='checkbox' ng-model="todo.done">
  {{todo.text}}
</label>

Working example: http://jsfiddle.net/b3NLH/1/

Solution 4 - Angularjs

Why dont you use

$watch('todo',function(.....

Or another solution would be to set the todo.done inside the ng-click callback and only use ng-click

<div ng-app="myApp" ng-controller="Ctrl">
<li  ng-repeat="todo in todos">
<input type='checkbox' ng-click='onCompleteTodo(todo)'>
    {{todo.text}} {{todo.done}}

and

$scope.onCompleteTodo = function(todo) {
        todo.done = !todo.done; //toggle value
        console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
        $scope.current = todo;
}

Solution 5 - Angularjs

Replacing ng-model with ng-checked works for me.

Solution 6 - Angularjs

It is kind of a hack but wrapping it in a timeout seems to accomplish what you are looking for:

angular.module('myApp', [])
    .controller('Ctrl', ['$scope', '$timeout', function ($scope, $timeout) {
    $scope.todos = [{
        'text': "get milk",
        'done': true
    }, {
        'text': "get milk2",
            'done': false
    }];

    $scope.onCompleteTodo = function (todo) {
        $timeout(function(){
            console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
            $scope.doneAfterClick = todo.done;
            $scope.todoText = todo.text;
        });
    };
}]);

Solution 7 - Angularjs

The ordering between ng-model and ng-click seems to be different and it's something you probably shouldn't rely on. Instead you could do something like this:

<div ng-app="myApp" ng-controller="Ctrl">
<li  ng-repeat="todo in todos">
<input type='checkbox' ng-model="todo.done" ng-click='onCompleteTodo(todo)'>
    {{todo.text}} {{todo.done}}
</li> 
    <hr>
        task: {{current.text}}
        <hr>
            <h2>Wrong value</h2>
         done: {{current.done}}
</div>

And your script:

angular.module('myApp', [])
    .controller('Ctrl', ['$scope', function($scope) {
 
        $scope.todos=[
            {'text': "get milk",
             'done': true
             },
            {'text': "get milk2",
             'done': false
             }
            ];
    
        $scope.current = $scope.todos[0];
        

       $scope.onCompleteTodo = function(todo) {
            console.log("onCompleteTodo -done: " + todo.done + " : " + todo.text);
    //$scope.doneAfterClick=todo.done;
    //$scope.todoText = todo.text;
       $scope.current = todo;

   };
}]);

What's different here is whenever you click a box, it sets that box as what's "current" and then display those values in the view. http://jsfiddle.net/QeR7y/

Solution 8 - Angularjs

> Usually this is due to another directive in-between your ng-controller > and your input that is creating a new scope. When the select writes > out it value, it will write it up to the most recent scope, so it > would write it to this scope rather than the parent that is further > away. > > The best practice is to never bind directly to a variable on the scope > in an ng-model, this is also known as always including a "dot" in > your ngmodel. For a better explanation of this, check out this video > from John: > > http://www.youtube.com/watch?v=DTx23w4z6Kc

Solution from: https://groups.google.com/forum/#!topic/angular/7Nd_me5YrHU

Solution 9 - Angularjs

I just replaced ng-model with ng-checked and it worked for me.

This issue was when I updated my angular version from 1.2.28 to 1.4.9

Also check if your ng-change is causing any issue here. I had to remove my ng-change as-well to make it working.

Solution 10 - Angularjs

.task{ng:{repeat:'task in model.tasks'}}
  %input{type:'checkbox',ng:{model:'$parent.model.tasks[$index].enabled'}}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsupercobraView Question on Stackoverflow
Solution 1 - AngularjskakoniView Answer on Stackoverflow
Solution 2 - AngularjsPeter HollingsworthView Answer on Stackoverflow
Solution 3 - Angularjsmusically_utView Answer on Stackoverflow
Solution 4 - AngularjsGuillaumeAView Answer on Stackoverflow
Solution 5 - AngularjszzjoveView Answer on Stackoverflow
Solution 6 - AngularjsBrian LewisView Answer on Stackoverflow
Solution 7 - AngularjsManny DView Answer on Stackoverflow
Solution 8 - AngularjsfergusrgView Answer on Stackoverflow
Solution 9 - AngularjsthatzpremView Answer on Stackoverflow
Solution 10 - AngularjsAndrew WC BrownView Answer on Stackoverflow