3

I am trying to understand the working of ng-if in contrast with ng-show. After reading the docs and going through the related stackoverflow question here, I understand that ng-if removes the DOM element and the scope variables inside that ng-if are removed.i.e ng-model variables inside the 'removed' ng-if element wont appear in $scope.

From the Angular ng-if docs:-

Note that when an element is removed using ngIf its scope is destroyed and a new scope is created when the element is restored. The scope created within ngIf inherits from its parent scope using prototypal inheritance. An important implication of this is if ngModel is used within ngIf to bind to a javascript primitive defined in the parent scope.

Consider following code snippet:-

<!doctype html>
<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.10/angular.min.js"></script>
  </head>
  <body  data-ng-app="myModule">    
    <div data-ng-controller="TestController">      
      <input name="first" type="number" data-ng-model="form.x" placeholder="Enter Number X"/>
      <input name="second" type="number" data-ng-if="form.x>5" data-ng-model="form.y" placeholder="Enter Number Y"/>
      <input type="button" value="Click" ng-click="save()"/>      
    </div>  
    <script type="text/javascript">     
        var myModule = angular.module("myModule",[]);
        myModule.controller("TestController",function($scope){
            $scope.form = {};
            $scope.form.x = 0;
            $scope.form.y = 0;
            $scope.save = function(){
                console.log($scope.form);
            };
        });             
    </script>
</html>

This is pretty simple use case - show the second number input field only when first is greater than 5.

The save button click delegates to 'save' function in the controller which simply prints out the 'form' object of $scope.

Problem:-

Input 1:- Enter x=6 and y=2 Output 1 : {x: 6, y: 2}

Input 2:- Enter x=3 Output 2 : {x: 3, y: 2}

I am not able to understand why 'output 2' still shows y =2. If its DOM has been removed, shouldn't the output be just {x:3} ?

What should I do if I want to remove (ngIf-removed) model from the scope?

Thanks

Community
  • 1
  • 1
Kumar Sambhav
  • 7,503
  • 15
  • 63
  • 86
  • The DOM has been removed, but not the JS variable `$scope.form.y`. – Blackhole Jun 04 '14 at 16:34
  • @Blackhole - Yes that's what the observation is. How this is explains 'Note that when an element is removed using ngIf its scope is destroyed and a new scope is created when the element is restored.' is my doubt. – Kumar Sambhav Jun 04 '14 at 16:40
  • 5
    `ng-if` creates a new scope. If you define your property on that scope it would get destroyed when the scope is destroyed. Currently your property is defined on parent. – Chandermani Jun 04 '14 at 16:43
  • @Chandermani : How can I get hold of the 'newly' created scope in my controller ? – Kumar Sambhav Jun 05 '14 at 04:21
  • The newly created scope existing within the start and end tag of element where it is declared. The parent scope cannot directly access the child scope. In your input if you add `ng-click` or `ng-change` and add a function, `this` within the function implementation would refer to the `ng-if` scope. – Chandermani Jun 05 '14 at 06:36

3 Answers3

5

Problem

To further what @Chandermani pointed out in comments, ng-if creates a new scope, which has its own variables. It does, however, prototypically inherit from its parent scope, so if you set a property on an existing parent object, such as what you're doing by using form.y, when the child scope is destroyed, that property remains unaffected.

Quick fix solution

You could add another directive to the same element as the one you're setting ng-if on, which deletes the property from the scope on $destroy:

Directive

myModule.directive('destroyY', function(){
  return function(scope, elem, attrs) {
    scope.$on('$destroy', function(){
      if(scope.form.y) delete scope.form.y;
    }) 
  }
});

View

<input ... data-ng-if="form.x>5" destroy-y .../>

Demo

Note: I saw that @user2334204 posted something similar. I decided to post this anyway because here the value of x won't have to be checked every digest

Marc Kline
  • 9,399
  • 1
  • 33
  • 36
3

Hi there :D you could set a watcher on your "x" variable, something like this:

$scope.$watch(function(){
      return $scope.form.x;
},function(){
      if($scope.form.x < 5) delete $scope.form.y;
});

Although i don't know if using "delete" is a good practice...

Hope it works for you.

----EDIT----

Another approach:

<input ng-model="form.x" ng-change="check(form.x)">

And in your controller:

$scope.check = function(x){
        if(x < 5 ) delete $scope.form.y;
};

Though i think @Marc Kline option is even better.

DSalme
  • 61
  • 4
0

For dynamic key, you can define a directive like below:

myModule.directive('removeKey', function () {
return {
    restrict: 'A',
    link: function (scope, element, attrs) {

        scope.$on('$destroy', function () {

            let attributes = scope.$eval(attrs.removeKey);

            if (scope.$parent[attributes.mainModel].hasOwnProperty(attributes.modelKey))
                delete scope.$parent[attributes.mainModel][attributes.modelKey];
        });
    }
  };
});

and your view something looks like this:

<div ng-if="condition === 0">
    <input ng-model="myFormJson.inputOne" remove-key='{"mainModel":"myFormJson","modelKey":"inputOne"}' />
</div>

<div ng-if="condition === 1">
    <input ng-model="myFormJson.inputTwo" remove-key='{"mainModel":"myFormJson","modelKey":"inputTwo"}' />
</div>
Reza
  • 493
  • 5
  • 14