Shubham Paramhans
Shubham Paramhans

Reputation: 85

Why ng-if creates so much issue in angularjs

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.

Upvotes: 2

Views: 2504

Answers (2)

Michael Kang
Michael Kang

Reputation: 52867

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.

Upvotes: 4

Nenad Vukicevic
Nenad Vukicevic

Reputation: 623

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".

Upvotes: 3

Related Questions