ng-init + ng-controller:控制器范围内的奇怪行为

时间:2013-06-14 08:50:27

标签: angularjs-scope

我是Angular的新手,但真的很喜欢它的方法。我有一个HTML文件,我在ng-init元素中使用<div>初始化变量,其中我还声明了一个带有ng-controller指令的控制器:

<div ng-controller="myCtrl" ng-init='foo="bar"'>

如果我console.log来自控制器脚本的$scope对象,我可以看到其他列出的foo属性,但是当我尝试从同一个脚本访问它时,它给了我undefined。我也在使用Batarang,它向我展示了<div>范围内的模型,该模型还包含foo属性。

我从Pass variables to AngularJS controller, best practice?的第二个答案中知道我可以通过将ng-init指令移到外部<div>来解决问题,但我想知道实际发生了什么在幕后。任何帮助都非常感谢,提前感谢。

修改

div元素中指令的顺序无关紧要。即使在ng-init

之前指定了ng-controller,问题仍然存在

3 个答案:

答案 0 :(得分:18)

好吧,我想我想出来了

由于Angular执行其编译阶段的方式,出现了ng-init在外部/内部els中的不同行为。编译包括不同的步骤。在这种情况下最相关的是:

  1. 控制器实例化
  2. 预链接
  3. postlinking
  4. 以每个DOM节点为基础以此顺序发生(即对于每个节点,控制器代码,如果存在,则在任何预链接,链接或后链接f之前执行)

    ng-init在指定的节点上注册pre - 链接f,$eval指令的内容(在我的示例中,f为{{{{{{ 1}} prop)。因此,当执行同一节点的控制器代码时,支柱尚不存在,这符合@ Aron的答案

    在编译阶段,Angular在深度优先的基础上从根向下遍历DOM,这意味着父级在子级之前编译。将foo指令放在外部el中允许子节点的控制器继承外部的作用域。这解释了'外部'黑客

    黑客@Aron指向在道具上注册观察者,这样,当道具在预链接阶段最终ng-init时,回调f可以找到它

    我建议基于异步JS和Angular特性的其他两个可能的hacks(参见this jsFiddle)。一个涉及使用$eval JS本地f,而另一个涉及更多'Angular'并转向setTimeout

    imho,Angular在声明意图方面对$evalAsync指令的实现存在缺陷。我已经破解了Angular的代码来试验各种各样的实现。这并不困难(即使在删除ng-init指令本机代码之前添加了2行代码),并且在应用于上面的jsFiddle中的代码时也能正常工作,但我还没有在复杂的应用程序上测试它。对于那些感兴趣的人,这是我正在做的事情(参考文献v 1.2.0-rc2):

      我在ng-init f块中的
    • 声明了一个未初始化的applyDirectivesToNode局部变量
    • 在同一个f中,在为本地nodeHasInitData var分配了directiveName prop值之后,我将对directive.name静态字符串进行测试,该字符串是Angular分配给它的规范化名称。在节点上声明它时的"ngInit"指令
    • 如果测试通过,我将ng-init设置为nodeHasInitData。如果测试失败,则无法完成任务( - true在关闭中仍然是nodeHasInitData
    • undefined f块中,在nodeLinkFn块之前检查节点中是否存在控制器(上面列表中的步骤1),我正在添加一个关于值的测试if(我可以这样做,因为nodeHasInitData内已定义nodeLinkFn
    • 如果测试通过,我调用applyDirectivesToNode,这是本地scope.$eval(attrs.ngInit)指令的预链接f。 ng-initscope都是attrs的原始参数,因此它们可用。如果测试失败,则无法完成任务
    • 这样,我已经将 1 初始化从步骤2移动到新的步骤0,在执行相应的控制器代码之前为其提供内部el范围

    <子>  1.实际上,我已经复制了它,因为nodeLinkFn指令定义的预链接f仍然存在。然而,这并不是很重要,我认为通过更改/删除指令对象可以轻松避免

    修改

    为了避免复制,出于上述黑客的说明目的,将ng-init Angular var的赋值代码替换为ngInitDirective

    是安全的。

答案 1 :(得分:2)

首先创建控制器,然后设置foo = bar。

这意味着在你的控制器的代码中,foo还没有存在。但是在调试时,它已经被创建了。

这是一种解决方法,但我认为这真的是一个黑客。 Why is the variable set in ng-init undefined in $scope in AngularJS?

imho修复此问题的角度方法不是将数据放入视图中。数据属于您的模型或控制器或服务。这是为了使关注点分离有效

答案 2 :(得分:1)

解决方法是使用ng-init的函数。

<div ng-app>
    <div ng-controller="MyCtrl" ng-init="init('kittens')">
        Page title is {{ pageTitle }}
    </div>
</div>

控制器:

var MyCtrl = function($scope) {   
    $scope.init = function(pageTitle) {
        $scope.pageTitle = pageTitle;
    }
};

我一直在使用ng-init为我的控制器指定一个类型 - 即控制器在应用程序中多次使用,但每次需要一条不同的信息。它节省了我为每种类型制作一个单独的控制器。

相关问题:

Why is the variable set in ng-init undefined in $scope in AngularJS?

How to set scope property with ng-init?