I made a recursive ng-repeat element, and trying to manipulate things has turned into a nightmare, because I don\'t have reference to the parent I\'m iterating over.
The
I would stay away from Angular's $parent
and all the $scope
creation and inheritance madness. What you need is to keep a reference to the parent object, not finding how many scopes are in-between ng-include
's and ng-repeat
's and juggling with $parent.$parent
. My suggestion is to explicitly save references to the values you are interested in, just implement it yourself with custom logic. For example, if you want to expose a value from the parent ng-repeat to the child ng-repeat
<!-- Explictly save the value into the parent var -->
<div ng-init="parent = value">
<!-- Save parent reference and
update the parent var to point to the new value -->
<div ng-repeat="(k,value) in value"
ng-init="value._parent = parent; parent = value;">
<div ng-repeat="(k,value) in value"
ng-init="value._parent = parent">
<button ng-click="doSomething(value)"></button>
</div>
</div>
</div>
Then in your javascript code you can access all parents recursively
function doSomething(value){
parent1 = value._parent;
parent2 = parent1._parent;
}
Same code with ng-include
<div ng-init="parent = value">
<div ng-repeat="(key,value) in value"
ng-init="value._parent = parent; parent = value;"
ng-include="'node.html'">
</div>
</div>
<script type="text/ng-template" id="node.html">
<div ng-if="!isLeaf(value)"
ng-repeat="(key,value) in value"
ng-init="value._parent = parent; parent = value;"
ng-include="'node.html'">
<button ng-if="isLeaf(value)" ng-click="doSomething(value)"></button>
</script>
ng-repeat
creates a new scope for each element.
ng-include
also creates a new scope.
When you use ng-include
together with ng-repeat
, there are 2 scopes created for each element:
ng-repeat
for current element. This scope holds the current element in the iteration.ng-include
which inherits from ng-repeat
's created scope. The $parent
property of current scope allows you to access its parent scope.
Essentially, scope created by ng-include
is empty, when you access value
and key
in your node.html
, it actually access the inherited values from parent scope created by ng-repeat
.
In your node.html, accessing {{value}}
is actually the same as {{$parent.value}}
. Therefore if you need to access the parent of the current element, you have to do one step further by accessing {{$parent.$parent.value}}
This DEMO will clear things up:
<script type="text/ng-template" id="node.html">
<div>
Current: key = {{key}}, value = {{value}}
</div>
<div>
Current (using $parent): key = {{$parent.key}}, value = {{$parent.value}}
</div>
<div>
Parent: key = {{$parent.$parent.key}}, value = {{$parent.$parent.value}}, isArray: {{isArray($parent.$parent.value)}}
</div>
<ul ng-if="isObject(value)"> //only iterate over object to avoid problems when iterating a string
<li ng-repeat="(key,value) in value" ng-include="'node.html'"></li>
</ul>
</script>
If you need to simplify the way to access the parent, you could try initializing the parent using onload
of ng-include
. Like this:
In your top level, there is no ng-if
=> only 2 level deep
<ul>
<li ng-repeat="(key,value) in json"
ng-include="'node.html'" onload="parent=$parent.$parent"></li>
</ul>
In your sub levels, there is ng-if
=> 3 levels deep:
<ul ng-if="isObject(value)">
<li ng-repeat="(key,value) in value"
ng-include="'node.html'" onload="parent=$parent.$parent.$parent"></li>
</ul>
Inside the template, you could access the parent using parent
, grandparent using parent.parent
and so on.
DEMO