63

I am trying to bind a checkbox to scope using ng-model. The checkbox's initial state corresponds to the scope model just fine, but when I check/uncheck the checkbox, the model does not change. Some things to note is that the template is dynamically loaded at runtime using ng-include

app.controller "OrdersController", ($scope, $http, $location, $state, $stateParams, Order) ->

  $scope.billing_is_shipping = false
  $scope.bind_billing_to_shipping = ->
    console.log $scope.billing_is_shipping


<input type="checkbox" ng-model="billing_is_shipping"/>

When I check the box the console logs false, when I uncheck the box, the console again logs false. I also have an order model on the scope, and if I change the checkbox's model to be order.billing_is_shipping, it works fine

chris
  • 5,365
  • 5
  • 38
  • 52

4 Answers4

159

I struggled with this problem for a while. What worked was to bind the input to an object instead of a primitive.

<!-- Partial -->
<input type="checkbox" ng-model="someObject.someProperty"> Check Me!

// Controller
$scope.someObject.someProperty = false
Matt
  • 1,622
  • 1
  • 10
  • 2
  • 3
    I had the same problem, and this fixed it. I wonder why primitives weren't working (as they do in the [official docs](https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D))... – acdcjunior Jun 29 '14 at 23:49
  • 3
    Thank you for this!! Not sure why this doesn't work with a primitive, could this just be a bug in Angular? – jbisa Jul 07 '14 at 18:37
  • 4
    WHYYYYYY!! Thanks man, I owe you beer and cookies. Also, I can one-up you all: I had four checkboxes on the same page, copy/paste stuff, same operations on different models.. Well, two worked and two did not! – mccc Jul 03 '15 at 13:38
  • @mccc I have that exact problem!! Hopefully this resolves it! – Ian Brindley Jul 07 '15 at 15:02
  • Guys, this answer a little be a hack. I suggest you read @zsong asnwer – Mediator Nov 04 '15 at 08:58
  • @Mediator nope, that's not the issue, one might notice if his variables are elsewhere.. Also, I'm back on this page because this keeps happening: does anyone have any insight on the issue? Getting frustrated – mccc Dec 03 '15 at 10:03
  • Hi, I used the same way and the value of the model gets updated in view. But the value is not being reflected in controller. It always shows false in controller. Pls suggest what could be causing this. – JS dev Jun 08 '16 at 14:09
  • Thanks for save my life :') – wryel Jun 19 '16 at 21:21
  • Thank you buddy, one more thing to learn with "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" – legramira Sep 15 '16 at 02:59
  • This worked for me too!, we also need to define the object in controller too. like $scope.someObject ={}; – Praveen Kumar Aug 05 '19 at 12:15
27

If the template is loaded using ng-include, you need to use $parent to access the model defined in the parent scope since ng-include if you want to update by clicking on the checkbox.

<div ng-app ng-controller="Ctrl">
    <div ng-include src="'template.html'"></div>
</div>

<script type="text/ng-template" id="template.html">
    <input type="checkbox" ng-model="$parent.billing_is_shipping" ng-change="checked()"/>
</script>

function Ctrl($scope) {
    $scope.billing_is_shipping = true;

    $scope.checked = function(){
        console.log($scope.billing_is_shipping);
    }
}

DEMO

zs2020
  • 52,221
  • 27
  • 148
  • 209
  • Also, I have bound several other properties in the same template without using $parent and it works for those – chris Sep 05 '13 at 17:31
  • If I initialize billing_is_shipping as true, the checkbox is initially checked, so it is getting the right value, but just not binding it on that checkbox for some reason – chris Sep 05 '13 at 17:34
  • Another thing I tried is duplicating the checkboxes and I discovered that checking one checks the other – chris Sep 05 '13 at 17:35
  • @Chris I created a demo for you. If you want to update the model from the inner scope, you do need `$parent` for that. – zs2020 Sep 05 '13 at 17:40
  • I tried that and it didn't work, see my above comments and see if anything else occurs to you? – chris Sep 05 '13 at 17:42
  • I also have an order model on the scope, and if I change the checkbox's model to be order.billing_is_shipping, it works fine – chris Sep 05 '13 at 17:42
  • @chris Can you just open the demo I created for you and compare it with your code? – zs2020 Sep 05 '13 at 17:42
  • @Chris Then there got be something else in your code is funky since the demo work fine without any problem. – zs2020 Sep 05 '13 at 17:43
  • I think the strangest thing is that it will bind to order.billing_is_shipping just fine and other bindings in the same template bind to $scope properties just fine as well, without using $parent – chris Sep 05 '13 at 17:44
  • @Chris The best way I can help you is if you can update the fiddle and show the rest of your code. – zs2020 Sep 05 '13 at 17:45
  • To explain, you are correct, including creates child scope which prototypically inherits from parent. $parent.billing_is_shipping wasnt working because I had ng-include nested in another ng-include. I suspect $parent.$parent.billing_is_shipping might have worked but i moved all my views into the first ng-include so $parent.billing_is_shipping works correctly now – chris Sep 05 '13 at 18:18
  • 1
    And solved my problem too, but why this suits for me while the real answer doesn't, just why? There must be some explanation. – Bahadir Tasdemir May 11 '16 at 12:49
9

In my directive (in the link function) I had created scope variable success like this:

link: function(scope, element, attrs) {
            "use strict";

            scope.success = false;

And in the scope template included input tag like:

<input type="checkbox" ng-model="success">

This did not work.

In the end I changed my scope variable to look like this:

link: function(scope, element, attrs) {
            "use strict";

            scope.outcome = {
                success : false
            };

And my input tag to look like this:

<input type="checkbox" ng-model="outcome.success">

It now works as expected. I knew an explanation for this, but forgot, maybe someone will fill it in for me. :)

ancajic
  • 4,504
  • 1
  • 41
  • 62
  • Voted up! I've been struggling with the same issue (we're both using directives) for almost two days now. This saved my life and my deadline. Thank you for sharing this! =o) – Dee J. Aug 12 '16 at 04:25
9

Expanding on Matt's answer, please see this Egghead.io video that addresses this very issue and provides an explanation for: Why binding properties directly to $scope can cause issues

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

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.

Community
  • 1
  • 1
Rk220
  • 181
  • 1
  • 10
  • 2
    This answer relies completely on external links. Should they ever become invalid, your answer would become useless. So please edit it and add at least a summary of what can be found there. Thank you! – Fabio says Reinstate Monica Mar 23 '16 at 00:10
  • @FabioTurati how prescient! the youtube video is inoperable now! – Dinesh Dec 07 '16 at 22:04
  • 1
    @Dinesh Really? It works for me. Anyway it isn't just my idea, it's a policy of SO's, and the reason is that at such a large scale (there are millions of answers here!) this problem is bound to happen, therefore we need to take countermeasures. – Fabio says Reinstate Monica Dec 07 '16 at 23:28