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



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

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

如果我从控制器脚本console.log $scope对象,我可以看到foo属性在其他列表中列出,但是当我试图从相同的脚本访问它时,它给了我undefined。我也在使用Batarang,它向我展示了<div> -作用域的模型,其中还包括foo属性。

我知道从第二个答案传递变量到AngularJS控制器,最佳实践?我可以通过将我的ng-init指令移动到外部<div>来解决问题,但我想知道幕后到底发生了什么。非常感谢任何帮助,提前感谢。

编辑

div元素中指令的顺序无关紧要。即使在ng-controller之前指定了ng-init,问题仍然存在

好的,我想我弄明白了

ng-init在外部/内部层的不同行为是由Angular执行编译阶段的方式引起的。编译由不同的步骤组成。在这种情况下最相关的是:

    <
  1. 控制器实例化/gh>
  2. prelinking
  3. postlinking

在每个domnode的基础上以这种顺序发生(即对于每个节点,控制器代码,如果存在,在任何前链接、链接或后链接f之前执行)

ng-init在其指定的节点上注册pre -link f,其中$eval是指令的内容(在我的示例中,f将值赋给foo prop)。因此,当执行同一节点的控制器代码时,prop还不存在,这与@Aron的答案

一致。

在编译阶段,Angular会按照深度优先的原则从根节点向下遍历DOM,这意味着父节点会在其子节点之前被编译。将ng-init指令放在外部节点中允许子节点的控制器继承外部节点的作用域。这解释了"外部el"hack

hack @Aron指向在prop上注册一个观察者,这样,当prop最终在预链接阶段被$eval求值时,回调函数f可以找到它

我建议另外两种基于异步JS和Angular特性的方法(参见这个jsFiddle)。一个涉及到使用setTimeout JS原生f,而另一个更"Angular",并诉诸$evalAsync

恕我直言,Angular对ng-init指令的实现在声明的意图方面有一个缺陷。我已经对Angular的代码进行了修改,以尝试不同的实现。这并不困难(添加了2行代码,甚至在可能删除ng-init指令本地代码之前),并且在应用于上述jsFiddle中的代码时工作,但我没有在复杂的应用程序上测试它。对于那些感兴趣的人,以下是我正在做的(参考v 1.2.0-rc2):

  • applyDirectivesToNodef块中我声明了一个未初始化的nodeHasInitData局部变量
  • 在相同的f中,在局部directiveName变量被赋予directive.name prop值之后,我根据"ngInit"静态字符串对它进行测试,这是Angular在
  • 节点上声明ng-init指令时分配给它的规范化名称。
  • 如果测试通过,我将nodeHasInitData设置为true。如果测试失败(-> nodeHasInitData在闭包中保留undefined),则不执行任何操作
  • nodeLinkFn f块中,在if块检查节点中是否存在控制器之前(上面列表中的步骤1),我添加了对nodeHasInitData值的测试(我可以这样做,因为nodeLinkFnapplyDirectivesToNode中定义)
  • 如果测试通过,我调用scope.$eval(attrs.ngInit),这是本机ng-init指令的预链接f所做的。scopeattrs都是nodeLinkFn的原生参数,所以它们是可用的。如果测试失败,什么也不做
  • 通过这种方式,我将1从步骤2的初始化移动到新的步骤0,它在执行相应控制器的代码之前提供内部el作用域

<子>1. 实际上,我已经复制了它,因为ng-init指令定义的预链接f仍然存在。然而,这并不是一个大问题,我认为通过更改/删除指令对象

可以很容易地避免它。

编辑

为了避免复制,安全的做法是将ngInitDirective Angular变量的赋值代码替换为var ngInitDirective = valueFn({});

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

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

这里有一个绕过它的方法,但我认为这真的是一个hack。为什么在AngularJS的$scope中ng-init中设置的变量未定义?

我认为解决这个问题的方法是不把数据放到视图中。数据属于你的模型、控制器或服务。这是为了保持关注点分离的有效性

一个解决方法是为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为我的控制器指定类型-即控制器在应用程序中使用多次,但每次它需要一个不同的信息。它节省了我为每种类型制作一个单独的控制器。

相关问题:

为什么在AngularJS的$scope中ng-init中设置的变量未定义?

如何使用ng-init设置作用域属性?

最新更新