ng-init + ng-controller: strange behavior in the controller's scope

angularjs-scope

Solution

ok, I think I figured it out

the different behavior of `ng-init` in outer/inner els arises because of the way Angular executes its compiling phase. compiling consists of different steps. the most relevant in this case are:

- controller instantiation

- prelinking

- linking

- postlinking

that take place in this order on a per-DOMnode basis (i.e. for each node, the controller code, if present, is executed before any prelink, link, or postlink f)

`ng-init` registers a `pre`-link f on the node it is specified in, which `$eval`s the directive's content (in my example, the f assigns a value to the `foo` prop). so, when the controller code for the same node is executed, the prop does not exist yet, which is in line with @Aron's answer

in the compile phase, Angular traverses the DOM from the root down on a depth-first basis, which means that parent els are compiled before their children. putting the `ng-init` directive in an outer el allows the controller of the child node to inherit the outer's scope. this explains the 'outer el' hack

the hack @Aron points to registers an observer on the prop, so that, when the prop is finally `$eval`uated in the prelink phase, the callback f can find it

I suggest two other possible hacks based on asynchronous JS and Angular features (see this jsFiddle). one involves using `setTimeout` JS native f, whereas the other is more 'Angular' and resorts to `$evalAsync`

imho, there's a flaw in Angular's implementation of the `ng-init` directive with respect to the declared intent. I have hacked the Angular's code to experiment a diverse implementation. It is not difficult (2 lines of code added, even before possibly removing the `ng-init` directive native code), and works when applied to the code in the jsFiddle above, but I have not tested it on complex apps. For those interested, here is what I'm doing (refs are to v 1.2.0-rc2):

- in the `applyDirectivesToNode` f block I declare a non-initialized `nodeHasInitData` local var

- in the same f, after the local `directiveName` var is assigned the `directive.name` prop value, I test it against the `"ngInit"` static string, which is the normalized name Angular assigns to the `ng-init` directive when it is declared on the node

- if the test passes, I set the `nodeHasInitData` to `true`. nothing is done if the test fails (-> `nodeHasInitData` remains `undefined` in the closure)

- in the `nodeLinkFn` f block, before the `if` block that checks for the presence of controllers in the node (step 1 in the list above), I'm adding a test on the value of `nodeHasInitData` (I can do that because `nodeLinkFn` is defined inside `applyDirectivesToNode`)

- if the test passes, I invoke `scope.$eval(attrs.ngInit)`, which is what the prelink f of the native `ng-init` directive does. both `scope` and `attrs` are native params of `nodeLinkFn`, so they are available. nothing is done if the test fails

- this way, I have moved1 the initialization from step 2 to a new step 0, which feeds the inner el scope before the corresponding controller's code is executed

1. Actually, I have replicated it, because the prelink f defined by the `ng-init` directive is still there. It is not a great deal, however, and I think it could be easily avoided by changing/removing the directive object

EDIT

To avoid replication, it is safe, for the illustrative purposes of the hack described above, to replace the assignment code of the `ngInitDirective` Angular var with `var ngInitDirective = valueFn({});`

Problem

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`

Original source

Related problems