AngularJS: ng-if | 隐藏(移除)ng-model变量未从$scope中移除

3
我正在尝试理解ng-if与ng-show的工作原理。 阅读文档并查看相关的stackoverflow问题here后,我了解到ng-if会删除DOM元素和其中的作用域变量。即在“已删除”的ng-if元素中的ng-model变量将不会出现在$scope中。
Angular ng-if docs可以得知:当使用ngIf删除元素时,其作用域会被销毁,并且当元素恢复时会创建一个新的作用域。在ngIf内部创建的作用域使用原型继承从其父作用域继承。这个重要的含义是如果在ngIf内使用ngModel来绑定到在父作用域中定义的JavaScript基元。
请考虑以下代码片段:
<!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>

这是一个很简单的用例 - 当第一个数字输入框大于5时,仅显示第二个数字输入框。
保存按钮点击将委托给控制器中的“save”函数,该函数仅打印出$scope的“form”对象。
问题:-
输入1:- 输入x = 6和y = 2 输出1:{x:6,y:2}
输入2:- 输入x = 3 输出2:{x:3,y:2}
我不明白为什么'output 2'仍然显示y = 2。如果它的DOM已被删除,输出不应该只是{x:3}吗?
如果我想从作用域中删除(ngIf-removed)模型,我该怎么办?
谢谢

DOM已被删除,但JS变量$scope.form.y未被删除。 - Blackhole
@Blackhole - 是的,这就是观察到的情况。我对“注意,当使用ngIf删除一个元素时,它的作用域会被销毁,并在元素恢复时创建一个新的作用域”这一点有疑问,不太明白是如何解释的。 - Kumar Sambhav
5
ng-if会创建一个新的作用域。如果您在该作用域上定义了属性,当该作用域被销毁时,该属性也会被销毁。目前,您的属性是在父级上定义的。 - Chandermani
@Chandermani:我该如何在控制器中获取“新创建的”作用域? - Kumar Sambhav
新创建的作用域存在于声明它的元素的开始和结束标记内。父级作用域无法直接访问子作用域。如果您在输入中添加 ng-clickng-change 并添加一个函数,则函数实现中的 this 将引用 ng-if 作用域。 - Chandermani
4个回答

5

问题

进一步解释@Chandermani在评论中指出的,ng-if创建了一个新的作用域,它有自己的变量。然而,它会原型继承它的父作用域,所以如果您在现有的父对象上设置属性,比如使用form.y,当子作用域被销毁时,该属性仍然不受影响。

快速修复解决方案

您可以在与您正在设置ng-if的相同元素上添加另一个指令,该指令在$destroy上从作用域中delete属性:

指令

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

视图

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

演示

注意:我看到@user2334204发布了类似的内容。 我决定发布这个,因为这里不需要每次digest都检查x的值。


3

你好:D 您可以在您的"x"变量上设置一个观察者,就像这样:

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

尽管我不确定使用“delete”是否是一个好习惯...希望它适用于你。----编辑----另一种方法:
<input ng-model="form.x" ng-change="check(form.x)">

在你的控制器中:

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

尽管我认为@Marc Kline的选项更好。

0

对于动态键,您可以定义以下指令:

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];
        });
    }
  };
});

你的视图大致如下:

<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>

0
我在我的情况下使用了ng-show而不是ng-if,它们的效果是一样的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接