AngularJS : ng-if | Hidden(Removed) ng-model variable not removed from $scope

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

Answer

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

Leave a Reply

Your email address will not be published. Required fields are marked *