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