ng-change on checkbox requires 2 clicks

steverippl picture steverippl · Feb 17, 2015 · Viewed 8.4k times · Source

I have a controller getting a list of users and a view listing them, along with a checkbox for displaying and setting whether they are active...

.controller('StaffCtrl', ['StaffService', function(StaffService) {
  var self = this;
  self.staff = StaffService.query();
  self.activeUpdate = function(person){
    StaffService.update({id:person.id}, person);
  };
}]);

<tr ng-repeat="person in staffCtrl.staff">
  <td>{{ person.name_full }} </td>
  <td><input type="checkbox" ng-checked="person.active" ng-model="person.active" ng-change="staffCtrl.activeUpdate(person)" ng-true-value="1" ng-false-value="0"></td>
</tr>

Clicking the checkbox once triggers activeUpdate, the async call is made and the user updated, but within the view the check mark itself doesn't disappear from the checkbox (in latest Chrome and Firefox). If I click it again now the check mark disappears and activeUpdate isn't called. If I click once more the check reappears and the call gets made all at the same time, always only when adding the check, removing it consistently takes 2 clicks!

What am I doing wrong?

Update: Sample JSON for the staff list (using curl to query the REST api...)

[
    {
        "active": 1,
        "id": 1,
        "name_first": "Ed",
        "name_full": "Tech, Ed",
        "name_last": "Tech",
        "name_middle": null,
        "uri": "/sips/api/staff/1",
        "username": "admin"
    },
    {
        "active": 1,
        "id": 252,
        "name_first": "test",
        "name_full": "aatest, test",
        "name_last": "aatest",
        "name_middle": "something",
        "uri": "/sips/api/staff/252",
        "username": "testteacher"
    },
    ...
]

Answer

Jason Goemaat picture Jason Goemaat · Feb 17, 2015

Using ng-checked and ng-model together can conflict, they both want to set the actual checked state of the checkbox. Second, ng-true-value and ng-false-value seem to only work with strings. So when you are clicking to set the value, it changes person.active from the number 1 to the string '1'. That is also what causes the unchecking problem, the string "0" is a true value so ng-checked thinks the box should be checked but ng-model does not.

And since ngTrueValue only accepts constant expressions, I don't see an easy way to get it to do what you want with numbers for active. (Angular Source)

You might be able to skip using ngModel and instead use ngChecked for display and ngClick to update your property with a method plunker:

<input type="checkbox" ng-checked="person.active" 
       ng-click="updateActive(person)"</input>

JS:

self.updateActive = function(person) {
  console.dir(person);
  person.active = 1 - person.active;
}