2

Today I came across a very strange problem, just trying to understand why was this happening
Below is my html

<div class="partial-container workflow-cont" ng-if="true">
<div class="clearfix mb20">
        <h3 class="heading-text pull-left pointer" ng-click="resetRole()" >{{'Roles' | translate}}</h3>
    </div>
    <div class="inner-container blue-border clearfix relative no-padding" ng-if="true">
        <div class="role-left-nav pull-left">
    .
    .
    .
    Some other html
    .
    .
    .
    <div class="any-or-select pull-none clearfix valign-middle">
    <p>
       <input type="radio" name="choice_p" id="low" ng-model="defineChoice" value="0" >
       <label for="low" class="ng-binding">{{'Absolute' | translate}}</label>
    </p>
    <p>
       <input type="radio" name="choice_p" id="medium" ng-model="defineChoice" value="1" >
       <label for="medium" class="ng-binding">{{'Attributes' | translate}}</label>
    </p>
    <p>
       <input type="radio" name="choice_p" id="high" ng-model="defineChoice" value="2"  >
       <label for="high" class="ng-binding">{{'Rule' | translate}}</label>
    </p>
  </div>

Now when I click radio buttons value of model defineChoice was changing successfully.
But when I try to do the same from controller $scope.defineChoice = 0 Its value was not changing back, however when i did console.log from controller it was coming to be zero but was not reflected in view.

But when I removed ng-if="true" from everywhere in view then everything went fine! Is this because of that fact that ng-if creates a child scope but if so then why was it working first time while init but later went sour.

  • possible duplicate of [Angularjs ng-model doesn't work inside ng-if](http://stackoverflow.com/questions/18342917/angularjs-ng-model-doesnt-work-inside-ng-if) – Nenad Vukicevic Jan 02 '15 at 09:42

2 Answers2

4

In Angular, models exist on a scope, and model bindings rely on a scope resolution algorithm involving prototypical scope inheritance. If a model binding resolves to the same model, regardless of the scope, then any changes to the model will affect all bindings. This is called two-way model binding: changes to the model affect the view and changes to the view affect the model.

Now, two-way model binding works as expected when the model is read-only. But in cases where it does not is usually because you are binding to a primitive, using ngModel with a writable control (i.e. ngModel and a radio button) and there is also a child scope involved (i.e. ngIf); two-way model binding breaks as soon as the model is written to. When the model is written to, it creates a model in the child scope with the same name as the model in parent scope. You now have two copies of the model, which can behave independently of each other.

How do you solve this?

Bind the ngModel to a property whose object is on scope - don't bind to a primitive on scope.

ng-model="someObj.defineChoice"

Sometimes this is also referred to as the "always have a dot in your ngModel" rule of thumb, which is not always necessary, but a good guideline, nonetheless.

When someObj is resolved, it will properly resolve the reference on the right scope (through prototypical scope inheritance), and then bind to the property of that reference. The important point is that the same model is resolved with each model binding, so that the two-way model binding works even when the model is written to.

Michael Kang
  • 52,003
  • 16
  • 103
  • 135
  • Got the point. But I want to understand why does primitive is not resolved in a new scope while a model of like "a.b" is resolved in a new scope. Is this a bug in scope resolution algorithm or problem with prototypical scope inheritance in javascript? – Shubham Paramhans Jan 02 '15 at 10:53
  • It has to do with the way javascript prototypical scope inheritance works and nothing to do with the scope resolution algorithm of AngularJS. It is not really a bug in javascript since javascript was designed to work this way for a long time. You can try this behaviour yourself - try creating a function that calls a nested function and try to read the property from a function higher up the chain, then try to write to it. – Michael Kang Jan 02 '15 at 10:55
3

ng-if creates a child scope, so ng-model directives within an ng-if are bound to the new scope. To access the parent scope use ng-model="$parent.defineChoice".

Nenad Vukicevic
  • 623
  • 6
  • 12