【问题标题】:Angular - Changing scope is not getting reflectedAngular - 改变范围没有得到反映
【发布时间】:2016-02-12 19:49:59
【问题描述】:

这很奇怪,因为它应该很简单。我会先贴出我的代码,然后再提问:

html -

<div ng-controller="myController" ng-switch on="addressCards">

    <div>
        {{addCustom}} // does not get changed
        <div ng-if="addCustom === false">

            {{addCustom}} // does get changed
            <button type="button" class="btn btn-primary btn-icon-text" ng-click="addCustom = true">
                <span class="icon icon-plus"></span>
                click here
            </button>
        </div>
    </div>
</div>

控制器 -

(function(){
'use strict';

angular.module('myApp')
    .controller('myController',['$scope',myController]);


function myController($scope){ 

    $scope.addCustom = false;

}


})();

所以我只是在我的控制器中引入了一个范围变量 - addCustom - 并将其默认设置为 false。此变量控制是否显示 div。我还在 2 个不同的位置输出 html 上的范围值。请参见上文。

但是当我在这个 div 中通过 ng-click 更改它的值时,它的值在第二个位置(在 div 内)发生变化,而不是在第一个位置(在 div 外)。因此,div 也不会改变状态。

我无法弄清楚这里可能有什么问题。有人可以帮忙吗?

【问题讨论】:

    标签: angularjs angularjs-scope prototypal-inheritance angular-ng-if


    【解决方案1】:

    发生的事情是当你有 ng-repeat,ng-switchng-if 指令时,无论放置在哪里,角度都会为这些元素创建子范围。那些新创建的范围在原型上是从那里的父范围继承的。

    对比Prototypal Inheritance是什么意思?

    如果您有范围层次结构,则 parent 范围属性可以在子范围内访问,前提是这些属性是对象(最初引用的对象被传递给子范围而不创建其新引用)。但是原始数据类型在子范围内是不可访问的,如果您查看代码addCustom 范围变量是原始数据类型。


    让我们讨论更多。

    这里有myController 控制器,它具有基本类型的addCustom 范围变量,正如我上面所说的ng-switchng-if 指令已编译,它们确实在该元素上创建了新的子范围。因此,在您当前的标记中,您在 ng-controller="myController" div 本身上有 ng-switch。对于内部 html,它创建了一个子范围。如果您想访问子范围内的父范围(原始类型),您可以在范围变量名称之前使用 $parent 表示法。现在您可以通过$parent.addCustom 访问addCustom 值。 当角度编译器来到ng-if div 时,这里还没有结束,它确实再次创建了新的子作用域。现在ng-if 的内部容器将再次具有子范围,其原型继承自父级。不幸的是,在您的情况下,您有原始 dataType 变量,因此您需要再次使用 $parent 表示法。所以在ng-if div 中,你可以通过$parent.$parent.addCustom 访问addCustom。这个$parent 的东西会解决你的问题,但是在 HTML 上使用它会使其不可读并与其父作用域紧密耦合(假设在 UI 上你会有 5 个子作用域,那么它看起来就像$parent.$parent.$parent.$parent 一样可怕)。因此,您应该采用以下方法。


    在定义ng-model 时关注Dot rule

    所以我想说你需要创建一些像$scope.model = {} 这样的对象并添加addCustom 属性。这样它就会遵循原型继承原则,并且子范围将使用由父级创建的相同对象。

    angular.module('myApp')
    .controller('myController',['$scope',myController]);
     function myController($scope){ 
        $scope.model = { addCustom : false };
    }
    

    在 HTML 上,您将使用 model.addCustom 而不是 addCustom

    标记

    <div ng-controller="myController" ng-switch on="addressCards">
        <div>
            {{model.addCustom}} // does not get changed
            <div ng-if="model.addCustom === false">
    
                {{model.addCustom}} // does get changed
                <button type="button" class="btn btn-primary btn-icon-text" ng-click="model.addCustom = true">
                    <span class="icon icon-plus"></span>
                    click here
                </button>
            </div>
        </div>
    </div>
    

    处理此类问题的其他最佳方法是,在 HTML 上使用控制器时使用 controllerAs 模式。

    标记

    <div ng-controller="myController as myCtrl" ng-switch on="addressCards">
        <div>
            {{myCtrl.addCustom}} // does not get changed
            <div ng-if="myCtrl.addCustom === false">
    
                {{myCtrl.addCustom}} // does get changed
                <button type="button" class="btn btn-primary btn-icon-text" ng-click="myCtrl.addCustom = true">
                    <span class="icon icon-plus"></span>
                    click here
                </button>
            </div>
        </div>
    </div>
    

    【讨论】:

    • 感谢您的回答。这很有意义。基本上你所说的是原型继承发生在对象而不是变量之间。当只使用变量时(就像我一样),ng-if 只是创建自己的范围。并且该值在更改时确实反映在父范围上。我的理解正确吗?
    • @SankarVikram 清除了你需要重新阅读答案的观点。我想我已经涵盖了你想要的所有观点。
    • @SankarVikram 你还在寻找关于这个问题的东西吗,你有多种选择来接受答案.. 你能不能这样做.. 如果他们面临类似的问题,这可以帮助其他人采取措施,谢谢:)
    【解决方案2】:

    来自文档:

    ngIf 中创建的范围使用prototypal inheritance 从其父范围继承。一个重要的含义是,如果在ngIf 中使用ngModel 来绑定到父作用域中定义的javascript 原语。在这种情况下,对子范围内变量所做的任何修改都将覆盖(隐藏)父范围中的值

    -- AngularJS ng-if directive API Reference

    经验法则是不要绑定到原语,而是绑定到对象

    范围继承通常很简单,您甚至不需要知道它正在发生...直到您尝试双向数据绑定(即,表单元素,ng-model) 从子范围内到在父范围上定义的原语(例如,数字、字符串、布尔值)。它不像大多数人期望的那样工作。发生的情况是子作用域获得了自己的属性,该属性隐藏/隐藏了同名的父属性。这不是 AngularJS 正在做的事情——这就是 JavaScript 原型继承的工作方式。新的 AngularJS 开发人员通常没有意识到 ng-repeatng-ifng-switchng-viewng-include 都创建了新的子作用域,因此当涉及这些指令时,问题通常会出现。 (有关问题的快速说明,请参阅 this example。)1

    遵循always have a '.' in your ng-models 的“最佳实践”可以轻松避免原语的这个问题 - 观看 3 分钟。 Misko 演示了 ng-switch 的原始绑定问题。1

    【讨论】:

    • 太棒了。感谢您的澄清。
    【解决方案3】:

    Ng-if 引入了不同的作用域。试试这个作为按钮的属性:

    ng-click="$parent.addCustom = false"
    

    这将确保您访问的是相同的范围。

    【讨论】:

    • @LukaJacobowitz 不,它不起作用,它需要是 $parent.$parent.addCustom 看看我在回答中的解释..
    【解决方案4】:

    正因为如此,使用ControllerAs 语法总是好的做法。所有属性都绑定到控制器对象并相应地命名空间,这意味着您永远不会遇到这个问题。我已使用 ControllerAs 语法更新了您的示例以演示其用法。

    HTML

    <div ng-controller="myController as vm" ng-switch on="addressCards">
      <div>
        {{vm.addCustom}}
    
        <div ng-if="vm.addCustom === false">
          {{vm.addCustom}}
          <button type="button" class="btn btn-primary btn-icon-text" ng-click="vm.addCustom = true">
            <span class="icon icon-plus"></span>
              click here
          </button>
        </div>
      </div>
    </div>
    

    控制器

    (function(){
      'use strict';
    
      angular.module('myApp')
        .controller('myController', [ myController ]);
    
      function myController () {
        var vm = this;
    
        vm.addCustom = false;
      }
    })();
    

    Here is an excellent article 提供有关ControllerAs 及其优势的更多详细信息。

    Classic Controller 和 Controller As 都有 $scope。理解这一点非常重要。无论哪种方法,您都不会放弃任何优点。真的。两者各有千秋。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 2020-11-05
      • 1970-01-01
      • 1970-01-01
      • 2020-10-19
      相关资源
      最近更新 更多