12

I'm new to Angular but really enjoying its approach. I have an HTML file where I am initializing a variable with ng-init in a <div> element, where I'm also declaring a controller with the ng-controller directive:

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

If I console.log the $scope object from the controller script I can see the foo property listed among the others, but when I try to access it from the same script it gives me undefined. I'm also using Batarang and it shows me a model for the <div>-scope that also includes the foo property.

I know from the second answer to Pass variables to AngularJS controller, best practice? that I can solve the problem by moving my ng-init directive into an outer <div>, but I would like to know what is really going on here behind the scenes. Any help greatly appreciated, thanks in advance.

EDIT

The order of the directives in the div element does not matter. The problem is still there even if ng-init is specified before ng-controller

4

3 回答 3

19

好的,我想我明白了

in external/inner els的不同行为ng-init是由于 Angular 执行其编译阶段的方式而产生的。编译由不同的步骤组成。在这种情况下最相关的是:

  1. 控制器实例化
  2. 预链接
  3. 链接
  4. 后链接

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

ng-initpre在它指定的节点上注册一个-link f ,它是指令的内容(在我的示例中, f 为prop$eval赋值)。foo因此,当执行同一节点的控制器代码时,该道具尚不存在,这与@Aron 的回答一致

在编译阶段,Angular 在深度优先的基础上从根向下遍历 DOM,这意味着父 el 在其子 el 之前编译。将ng-init指令放在外部 el 中允许子节点的控制器继承外部的范围。这解释了'outer el' hack

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

我建议基于异步 JS 和 Angular 特性的另外两种可能的 hack(参见这个 jsFiddle)。一种涉及使用setTimeoutJS 原生 f,而另一种则更“Angular”并诉诸$evalAsync

ng-init恕我直言,关于声明的意图,Angular 的指令实施存在缺陷。我已经破解了 Angular 的代码来试验不同的实现。这并不难(添加了 2 行代码,甚至在可能删除ng-init指令本机代码之前),并且在应用于上面 jsFiddle 中的代码时工作,但我没有在复杂的应用程序上测试它。对于那些感兴趣的人,这就是我正在做的事情(参考 v 1.2.0-rc2):

  • applyDirectivesToNodef 块中,我声明了一个未初始化的nodeHasInitData本地变量
  • 在同一个 f 中,在为本地directiveNamevar 分配directive.nameprop 值之后,我针对静态字符串对其进行测试,静态字符串是 Angular在节点上声明指令时"ngInit"分配给指令的规范化名称ng-init
  • 如果测试通过,我将其设置nodeHasInitDatatrue. 如果测试失败则不做任何事情(->nodeHasInitDataundefined在闭包中)
  • nodeLinkFnf 块中,在if检查节点中是否存在控制器的块之前(上面列表中的第 1 步),我正在添加对值的测试nodeHasInitData(我可以这样做,因为nodeLinkFn在内部定义applyDirectivesToNode
  • 如果测试通过,我调用scope.$eval(attrs.ngInit),这就是本机ng-init指令的预链接 f 所做的。两者scopeattrs都是 的本机参数nodeLinkFn,因此它们是可用的。如果测试失败则什么都不做
  • 这样,我将1从第 2 步的初始化移到了新的第 0 步,它在执行相应控制器的代码之前提供了内部 el 范围

1.其实我已经复制了,因为ng-init指令定义的prelink f还在。但是,这并不是什么大问题,我认为可以通过更改/删除指令对象轻松避免

编辑

为了避免复制,出于上述hack的说明性目的,将ngInitDirectiveAngular var的赋值代码替换为var ngInitDirective = valueFn({});

于 2013-09-13T10:45:13.980 回答
2

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

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

这是一种解决方法,但我认为这确实是一个hack。 为什么在 AngularJS 的 $scope 中未定义 ng-init 中设置的变量?

恕我直言,解决此问题的角度方法不是将数据放入视图中。数据属于您的模型或控制器或服务。这是为了保持关注点分离有效

于 2013-09-11T02:13:35.257 回答
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 为我的控制器指定一个类型——即控制器在应用程序中被多次使用,但每次它都需要一个不同的信息。它省去了我为每种类型制作单独的控制器。

相关问题:

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

如何使用 ng-init 设置范围属性?

于 2014-09-19T01:26:15.200 回答