2

I'm running into a roadblock. I have a simple piece of code:

<div ng-app>
    <input type="checkbox" ng-model="element" />Hide Element
    <div ng-if="!element">
        <input type="text" ng-disabled="disable" />       
        <input type="checkbox" ng-model="disable" />Disable
    </div>
</div>

This works fine, but I want the text and second checkbox split into multiple divs, both still dependent on the first checkbox, like:

<div ng-app>
    <input type="checkbox" ng-model="element" />Hide Element
    <div ng-if="!element">
        <input type="text" ng-disabled="disable" />
    </div>
    <div ng-if="!element">        
        <input type="checkbox" ng-model="disable" />Disable
    </div>
</div>

When I do this, the model does not get applied correctly and I'm unable to disable the text box by using the second checkbox. Am I misunderstanding the scope, or is this a bug?

I know with this example, I could wrap the two in an outer div, but my issue is that my structure is a table structure (yes, it's tabular data), where I don't want to hide an entire row, while keeping my markup as semantic as possible.

<table>
    <tr>
        <td></td> //this does not get hidden
        <td></td> //this does not get hidden
        <td></td> //this does not get hidden
        <td></td> //THIS GETS HIDDEN
        <td></td> //THIS GETS HIDDEN
    </tr>
</table>

If you want to play, I set up a basic fiddle: http://jsfiddle.net/qkmv6wfh/

PSL
  • 123,204
  • 21
  • 253
  • 243
Chad
  • 5,308
  • 4
  • 24
  • 36

2 Answers2

3

It is not a bug. The behavior you are seeing is because of the child scope created by ng-if. In your case the value set at the scope property disable is only available inside the child scope created by the second ng-if="!element" and disable inside the other block is in its own child scope. You can resolve it by setting it on an object reference where the object is initialized before any of these child scopes (ex ng-init="action ={}"), in this case both the child scopes refer to the same object reference action (as they get prototypically inherited) and modification on the property action.disabled gets reflected in both the places.

This can happen not just for ng-if any directive that creates a child scope like ng-repeat, ng-switch etc.

Read this well explained answer.

Try:

<div ng-app ng-init="action ={}">
  <input type="checkbox" ng-model="element" />Hide Element
  <div ng-if="!element">
    <input type="text" ng-disabled="action.disable" />
  </div>
  <div ng-if="!element">
    <input type="checkbox" ng-model="action.disable" />Disable
  </div>
</div>

Bad Code Alert: I have used ng-init only for this demo purpose, you should not use ng-init for initializing. Always use a controller to initialize values on the scope.

Demo

angular.module('app', []).controller('Controller1', function() {

}).controller('dummy', function() {}).controller('Controller2', function($scope) {
  $scope.action = {};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
  <p>With ng-init</p>
  <div ng-init="action ={}" ng-controller="dummy">
    <input type="checkbox" ng-model="element" />Hide Element
    <div ng-if="!element">
      <input type="text" ng-disabled="action.disable" />
    </div>
    <div ng-if="!element">
      <input type="checkbox" ng-model="action.disable" />Disable
    </div>
  </div>

  <p>With Controller As</p>
  <div ng-controller="Controller1 as ctrl">
    <input type="checkbox" ng-model="ctrl.element" />Hide Element
    <div ng-if="!ctrl.element">
      <input type="text" ng-disabled="ctrl.disable" />
    </div>
    <div ng-if="!ctrl.element">
      <input type="checkbox" ng-model="ctrl.disable" />Disable
    </div>
  </div>

  <p>With Controller and dot in the binding</p>
  <div ng-controller="Controller2">
    <input type="checkbox" ng-model="element" />Hide Element
    <div ng-if="!element">
      <input type="text" ng-disabled="action.disable" />
    </div>
    <div ng-if="!element">
      <input type="checkbox" ng-model="action.disable" />Disable
    </div>
  </div>
</div>
Community
  • 1
  • 1
PSL
  • 123,204
  • 21
  • 253
  • 243
  • Yeah, this was one solution I found. Another was to use `$parent.disable`. I just wasn't sure if this was intended behavior or not. – Chad Jan 02 '15 at 20:56
  • @Using $parent is bad. Imagine the case you have many child scopes layering up. You would need to do $parent.$parent.... If you have a controller and a method on the controller which you are calling to determine the state you wont face this issue because you are actually refereing tp the scope in the controller's closure. – PSL Jan 02 '15 at 20:57
  • is it bad just because it changes sometimes? I'd prefer not to set extra variables if I don't have to, and I'm the one who controls my scope ;) – Chad Jan 02 '15 at 20:58
  • @Chad yes you got the point. It is bad because that piece of code is completely dependent upon what its parent is, and it can get so messy when you have more directive that creates child scopes covering it. This is where `controller As` syntax enforces you to have a `.` in the binding and make sure you are modifying the right scope property. – PSL Jan 02 '15 at 21:02
-1

Try with ng-show/hide instead:

  <div ng-app>
    <input type="checkbox" ng-model="element" />Hide Element
    <div ng-hide="element">
        <input type="text" ng-disabled="disable" />
    </div>
    <div ng-hide="element">        
        <input type="checkbox" ng-model="disable" />Disable
    </div>
</div>
Karthik
  • 1,377
  • 6
  • 8
  • OP is asking for what is causing the behavior, not an alternative work around, and this defeats the purpose of this question. – PSL Jan 02 '15 at 21:13
  • Gotcha.. Just wanted to throw a different approach to avoid child scopes. It could have been more descriptive.. tnks – Karthik Jan 02 '15 at 21:15
  • Why avoid child scopes, why should you be wary about it? Leave this small example, you cannot write an entire app without creating/using a directive that does not create a child scope.. :) – PSL Jan 02 '15 at 21:20