20

I am trying to understand ng-if and scopes. As I am aware, ng-if creates a new child scope. Here is my issue:

View

<input ng-model="someValue1" />
<div ng-if="!someCondition">
    <input ng-model="$parent.someValue2" />
</div>

Controller

$scope.someCondition = true;

if ($scope.someCondition) {
    $scope.someValue2 = $scope.someValue1;        
}

If someCondition is set to true, then someValue2 should be the same as someValue1.

My problem is that I can't access someValue2 in both situations (true or false). How could I achieve this?

be-codified
  • 5,704
  • 18
  • 41
  • 65
  • 3
    In angular you must never ever modify $parent properties value directly. However you can modify property of $parent properties or you'll break inheritance. do : `$parent.someValue.num = 10`, don't : `$parent.someValue = 10` – Freezystem Jun 11 '15 at 17:16
  • This statement is not backed up in any way. Modifying `$parent` properties seems to work perfectly fine. The "already answered here" question at the very top has an answer that explains how modifying `$parent` properties works, even with some visualization, and it clearly shows that it works. One problem is that when adding more conditions and `ng-if`s or creating some other child scopes, there may be a longer chain of `$parent`s and the code breaks, unlike when using objects. Actually, you don't even need `$parent` when using those objects, just use `$scope`: `$scope.someValue.num = 10`. – Scar Jul 21 '20 at 09:07

3 Answers3

33

Yes, ng-if creates a new child scope

To watch a model property in an ng-if, the rule-of-thumb is:

DO NOT USE THE SCOPE AS MODEL

e.g.

ng-if='showStuff' //here my scope is model **INCORRECT**
ng-if='someObject.showStuff' // ** CORRECT **

Use an object property in ng-model - then, even if ng-if creates the new child scope, the parent scope will have the changes.

To see a working Plunker, look here : http://jsfiddle.net/Erk4V/4/

Jimmy Breck-McKye
  • 2,923
  • 1
  • 22
  • 32
Ankur Soni
  • 746
  • 6
  • 17
  • Why does this work? Both `$scope` and `someObject` are just objects, right? What makes `someObject` different? I am intending to name it `scope` in my project and it seems rather silly to have both `$scope` and `scope`. – Scar Jul 21 '20 at 08:32
  • I checked out the "already answered here" question at the very top and its answer provided some insight :) – Scar Jul 21 '20 at 08:45
16

ngIf does indeed create a new scope using prototypal inheritance. What that means is that the ngIf's scope's prototype object is that of its parent's scope. So if the attribute isn't found on the ngIf instance of its scope it will look into its prototype objects chain for that attribute. However, once you assign an attribute to the instance of the scope it will no longer look into its inheritance chain for the attribute. Here's a link explaining prototypal inheritance used in JS: https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance

How to solve this:

Parent controller:

$scope.data = {someValue: true};

Child controller:

$scope.data.someValue = false

Because you're not hiding an attribute on its parent's scope, you're just mutating an object on its parent's scope, this will indeed alter the parent's data object. So in your case:

<input ng-model="data.someValue1" />
<div ng-if="!data.someCondition">
    <input ng-model="data.someValue2" />
</div>
76484
  • 8,498
  • 3
  • 19
  • 30
user133688
  • 6,864
  • 3
  • 20
  • 36
-9

From what I'm aware of, the ng-if is purely a display level statement. You can use it to make some elements visible / invisible given certain values but I don't think it creates any kind of scope. What your HTML code will do is toggle the visibility of your secondary input.

If you'd like to switch your Value 2 to equal Value 1 whenever "someCondition" changes between false and true, then you can use $watch with something like this:

$scope.$watch(someCondition, function(){
  if (someCondition){
    $scope.someValue1 = $scope.someValue2
  }
})
FrankieAvocado
  • 333
  • 1
  • 3
  • 10
  • Thank you for your comment. My condition is on scope as well, so i dont think i need to watch it. – be-codified Jun 11 '15 at 17:27
  • No. ngIf differs from ngShow, ngHide in that it doesn't just hide the html element. It completely destroys it. And it does indeed create a new scope using prototypal inheritance. https://docs.angularjs.org/api/ng/directive/ngIf – user133688 Jun 11 '15 at 17:43
  • 1
    @user2734679 Nice dude, thanks for the link! I learn something new about Angular every day :-) – FrankieAvocado Jun 11 '15 at 18:11
  • this answer should be deleted – user2954463 Sep 27 '17 at 19:45
  • `ng-if` has its own scope , and `$watch` in controller won't watch it, unless you are using it into directive that has `ng-if` scope. – ahsan ayub Sep 14 '18 at 06:02
  • @Anyone who has down voted me, at least let me know, what i said wrong? A down vote does not help anyone. – ahsan ayub Sep 14 '18 at 06:17
  • `ng-if` does create a scope, counter to what this answer suggests. But this is actually the only answer that legitimately addresses the problem in the original question: the code that checks for `someCondition` only runs once when setting up the controller and so it doesn't check again when `someCondition` changes! A watch does solve this problem, though, I would suggest using explicitly executing code on user input; I usually use `ng-click`. I believe using this answer completely solves the problem in the original question as it was stated. – Scar Jul 21 '20 at 08:59