211

Here is the fiddle showing the problem. http://jsfiddle.net/Erk4V/1/

It appears if I have an ng-model inside of an ng-if, the model does not work as expected.

I am wondering if this is a bug or if I am misunderstanding the proper usage.

<div ng-app >
    <div ng-controller="main">

        Test A: {{testa}}<br />
        Test B: {{testb}}<br />
        Test C: {{testc}}<br />

        <div>
            testa (without ng-if): <input type="checkbox" ng-model="testa" />
        </div>
        <div ng-if="!testa">
            testb (with ng-if): <input type="checkbox" ng-model="testb" />
        </div>
        <div ng-if="!someothervar">
            testc (with ng-if): <input type="checkbox" ng-model="testc" />
        </div>

    </div>
</div>
Justin Carlson
  • 2,670
  • 2
  • 18
  • 19
  • 6
    For a workaround you can use ng-show="CONDITION" instead of ng-if. It should work. – Hari Das Jun 03 '16 at 06:33
  • I presume this is no longer an issue now that one can use `controllerAs`? – jamiebarrow Jul 26 '16 at 17:09
  • I had the same problem when using a directive with implicit `scope:false` and I added `ng-if` element around the directive - the scopes were bound initially, but they became separated after a watcher updated one of the scope values... – Aprillion Sep 14 '16 at 13:22

6 Answers6

224

The ng-if directive, like other directives creates a child scope. See the script below (or this jsfiddle)

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular.min.js"></script>

<script>
    function main($scope) {
        $scope.testa = false;
        $scope.testb = false;
        $scope.testc = false;
        $scope.obj = {test: false};
    }
</script>

<div ng-app >
    <div ng-controller="main">
        
        Test A: {{testa}}<br />
        Test B: {{testb}}<br />
        Test C: {{testc}}<br />
        {{obj.test}}
        
        <div>
            testa (without ng-if): <input type="checkbox" ng-model="testa" />
        </div>
        <div ng-if="!testa">
            testb (with ng-if): <input type="checkbox" ng-model="testb" /> {{testb}}
        </div>
        <div ng-if="!someothervar">
            testc (with ng-if): <input type="checkbox" ng-model="testc" />
        </div>
        <div ng-if="!someothervar">
            object (with ng-if): <input type="checkbox" ng-model="obj.test" />
        </div>
        
    </div>
</div>

So, your checkbox changes the testb inside of the child scope, but not the outer parent scope.

Note, that if you want to modify the data in the parent scope, you'll need to modify the internal properties of an object like in the last div that I added.

Benny Bottema
  • 11,111
  • 10
  • 71
  • 96
Jon7
  • 7,165
  • 2
  • 33
  • 39
  • 1
    How would I access the ng-if's scope from within the main controllers function? A bit frustrating. What's the reason for this? – Justin Carlson Aug 20 '13 at 19:05
  • Nevermind, @sza just answered that ^ question. I'm going to mark this answer as correct though, since it explains the exact reason I was having issues. – Justin Carlson Aug 20 '13 at 19:11
  • 21
    This is one reason it's fairly common to use a containing object on your scope, rather than modifying scope properties directly, as pointed out in the example: `$scope.obj = {...}` and `ng-model="obj.someProperty"` overcomes this limitation. – wulftone Oct 22 '14 at 22:23
206

You can use $parent to refer to the model defined in the parent scope like this

<input type="checkbox" ng-model="$parent.testb" />
zs2020
  • 53,766
  • 29
  • 154
  • 219
  • 16
    then i have `ng-model="$parent.$parent.foo` because I'm already inside a scope with an `ng-repeat` -- is this really the best way? – chovy Nov 25 '13 at 03:58
  • 4
    this really is confusing. why is that? and why doesn't ng-hide have it's own scope? – Dominik Goltermann Mar 03 '14 at 14:57
  • 5
    Re @Gaul: presumably because ng-hide/ng-show operates on the present DOM and just add/remove a CSS class, while ng-if/ng-switch/ng-repeat all muck with the DOM and keep track of extra state. Seems sensible. – trisweb Aug 19 '14 at 20:58
  • 4
    Sensible is **not** the word I use. – Jonathan Dumaine Dec 11 '14 at 21:54
  • 3
    Add an object to the original scope, and change properties of that object. e.g. ng-model="myObject.property". This will sidestep all the scope/$parent inanity. Google "angular dot rule" for more info. – Asmor Feb 16 '16 at 14:17
  • I think this answer is preferable to the selected one, because using `$parent` makes it clear that you are referring to the parent scope. Nesting a data structure (`obj.test`) merely circumvents the issue, whereas `$parent` acknowledges what's going on and applies the tools Angular provides for this exact situation. – dmbaughman Mar 30 '18 at 21:31
  • @dmbaughman Using a property on an object allows you to write code that doesn’t care how deeply it is nested. If you’re writing all of this in one logical angular view, then the instant you put `ng-if` somewhere, you need to remember to prefix **everything** inside of that with `$parent.`. Using an object property lets you reduce the amount of lines of code to change to, e.g., introduce an `ng-if`. – binki Jun 26 '18 at 18:55
51

You can use ngHide (or ngShow) directive. It doesn't create child scope as ngIf does.

<div ng-hide="testa">
Vasiliy Kevroletin
  • 1,188
  • 13
  • 17
  • 3
    Why does `ngIf` then create a child scope? Seems very strange to me. – freeall Jun 30 '15 at 09:37
  • 6
    Pay attention to comments of zsong answer. `ng-hide` doesn't change html structure. It simply changes css styles. `ng-if` is more complex: it removes and inserts html parts depending on condition. It creates child scope to store state (at least it should store hidden html part). – Vasiliy Kevroletin Jun 30 '15 at 12:08
  • Yup, Work for me – Basit Apr 02 '20 at 10:21
8

We had this in many other cases, what we decided internally is to always have a wrapper for the controller/directive so that we don't need to think about it. Here is you example with our wrapper.

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular.min.js"></script>

<script>
    function main($scope) {
        $scope.thisScope = $scope;
        $scope.testa = false;
        $scope.testb = false;
        $scope.testc = false;
        $scope.testd = false;
    }
</script>

<div ng-app >
    <div ng-controller="main">

        Test A: {{testa}}<br />
        Test B: {{testb}}<br />
        Test C: {{testc}}<br />
        Test D: {{testd}}<br />

        <div>
            testa (without ng-if): <input type="checkbox" ng-model="thisScope.testa" />
        </div>
        <div ng-if="!testa">
            testb (with ng-if): <input type="checkbox" ng-model="thisScope.testb" />
        </div>
        <div ng-show="!testa">
            testc (with ng-show): <input type="checkbox" ng-model="thisScope.testc" />
        </div>
        <div ng-hide="testa">
            testd (with ng-hide): <input type="checkbox" ng-model="thisScope.testd" />
        </div>

    </div>
</div>

Hopes this helps, Yishay

Yishay Haspel
  • 81
  • 1
  • 2
4

Yes, ng-hide (or ng-show) directive won't create child scope.

Here is my practice:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular.min.js"></script>

<script>
    function main($scope) {
        $scope.testa = false;
        $scope.testb = false;
        $scope.testc = false;
        $scope.testd = false;
    }
</script>

<div ng-app >
    <div ng-controller="main">

        Test A: {{testa}}<br />
        Test B: {{testb}}<br />
        Test C: {{testc}}<br />
        Test D: {{testd}}<br />

        <div>
            testa (without ng-if): <input type="checkbox" ng-model="testa" />
        </div>
        <div ng-if="!testa">
            testb (with ng-if): <input type="checkbox" ng-model="$parent.testb" />
        </div>
        <div ng-show="!testa">
            testc (with ng-show): <input type="checkbox" ng-model="testc" />
        </div>
        <div ng-hide="testa">
            testd (with ng-hide): <input type="checkbox" ng-model="testd" />
        </div>

    </div>
</div>

http://jsfiddle.net/bn64Lrzu/

AbcAeffchen
  • 14,400
  • 15
  • 47
  • 66
Xiayan Y
  • 61
  • 1
0

You can do it like this and you mod function will work perfect let me know if you want a code pen

  <div ng-repeat="icon in icons">                   
                <div class="row" ng-if="$index % 3 == 0 ">
                    <i class="col col-33 {{icons[$index + n].icon}} custom-icon"></i>
                    <i class="col col-33 {{icons[$index + n + 1].icon}} custom-icon"></i>
                    <i class="col col-33 {{icons[$index + n + 2].icon}} custom-icon"></i>
                </div>
         </div>
Cesar Vega
  • 455
  • 6
  • 15