0

Where exactly is this ng-init variable 'someData' stored?

<tr data-ng-repeat="item in items" ng-init="someData=1">
 <td>{{item.id}}</td>
 <td>{{someData}}</td>
</tr>

How can I access it from outside, ie from a function that should set someData = someData + 1?

Vok
  • 457
  • 5
  • 19

1 Answers1

0

Variables defined in a view are defined on the current scope. However, it's not always so easy to know what you current scope actually is. Your current scope is decided by the closest element that creates a scope.

The simple case

If you are directly inside of a controller then your current scope is the $scope variable of that controller. As in the following example:

In your HTML
<div ng-controller="MyController">
    <div ng-init="someData = 1">
    </div>
</div>
In MyController
$timeout(function() {
    console.log($scope.someData); // Logs 1
})

The variable "someData" was defined on the scope of MyController. Note the timeout, this is needed because the controller is run before the view is parsed, so if we log immediately the ng-init will not have run yet and the variable is undefined.

If you were to nest two controllers and then do ng-init inside that it would assign to the closest one. So if you have controller A and inside that you have controller B and inside that you do ng-init, then it would assign the variable inside controller B, not A.

The less simpel case

If you try the above solution with your code it wouldn't work. The reason is that you are using a repeater. A ng-repeat, along with most other directives will create their own scopes! In your case someData will be defined on the internal scope of the repeater, which is not accessible by your controller! This is btw where the "item" variable from the repeat is stored, in the local repeater scope for that row. Each row in the repeater gets it's own new scope, that is why you use the same "item" variable on each row but it means different things each time.

<div ng-controller="MyController">
    We are now in the scope of MyController.
    Variables defined here can be find in $scope of MyController.

    <div ng-repeat="foo in bar">
        We are now in the local repeater scope, one for each row.
        Note that the containing div is part of the scope.
        Variables defined here go on the local scope, MyController can't find them.
    </div>

    The repeater scope has ended, we are back in MyController

    <div ng-include="someview.html">
       And now we are in the local scope of the ng-include!
       This scope works just like the ng-repeat one does.
    </div>
</div>

This has to do with how inheritance works in javascript. Note that if you where to get a variable that only exists in a parent (say be printing it out) it would work fine. In javascript when you get a variable it will step up the inheritance-chain until it finds it. Vut as soon as you try to assign a value to something it creates it's own local variable and from then on uses that instead.

So in your case you couldn't actually find it from your controller (unless you sent it as a parameter to a function of course) because a parent scope cannot access child-scopes.

A better way

There are a few ways around this. The recommended way is to use the controller as syntax. It is described in the documentation for ng-controller, but in short you assign your variables directly to the controller object (instead of on the $scope object) and then give your controller a name when calling it in your view. This lets you be explicit about which variable from which controller you are after.

In MyController
this.data = {};
In your HTML
<div ng-controller="MyController as MC">
    <div ng-controller="item in items" ng-init="MC.someData = 1">
    </div>
</div>

Since you are now explicitly creating someData on your "MC" controller there is no more confusion. You know exactly where the data ends up no matter how many directives or controllers you have nested things in. Note that "item" is still inside the repeaters scope, but that is ok since it's supposed to be internal to the repeater. Pass it out using a function, or in theory an ng-init inside your repeater, if you ever need it.

Community
  • 1
  • 1
Erik Honn
  • 7,576
  • 5
  • 33
  • 42