Как получить родительский элемент внутри ng-include при повторении в ng-repeat рекурсивно
Я сделал рекурсивный элемент ng-repeat, и попытка манипулировать вещами превратилась в кошмар, потому что у меня нет ссылки на родителя, который я повторяю.
ng-repeat выглядит следующим образом:
ng-repeat="(key, value) in value"
Помните, что он рекурсивный, поэтому каждое значение в значении становится новым значением, поэтому я не могу просто использовать значение "in" из ng-repeat. Я хочу делать такие вещи, как проверка, является ли родитель массив, но $parent - это нечто странное, а не родительский элемент текущего значения итерации.
Некоторые примеры того, что я хочу сделать, это:
ng-show="isArray(parent)"
ng-click="delete(parent, $index)"
(в качестве примера того, что я делаю как работа, мне пришлось добавить свойство ___ISARRAYELEMENT___ для каждого из моих элементов массива, просто чтобы узнать, что это в массиве > . > ;;)
EDIT: В основном я хочу знать, есть ли какие-либо удобные метаданные в контексте ng-repeat, который мне не хватает.
EDIT: Хорошо, вот html целиком:
<html ng-app>
<head>
<title>JSON CRUD</title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js"></script>
<script src="angularjsoncrud.js"></script>
<script type="text/ng-template" id="node.html">
<button ng-click="minimized = !minimized" ng-show="!isLeaf(value)" ng-init="minimized=false" ng-class="{'glyphicon glyphicon-plus': minimized, 'glyphicon glyphicon-minus': !minimized}">-</button>
<input ng-model="value.___KEY___" ng-if="!isArrayElement(value)" />
<input ng-if="isLeaf(value)" ng-model="value.___VALUE___" />
<ul ng-if="!isLeaf(value)" ng-show="!minimized">
<li ng-repeat="(key,value) in value" ng-if="key != '___KEY___' && key != '___ISARRAY___'" ng-include="'node.html'"></li>
<button type="button" ng-if="isArray(value)" ng-click="addToArray(value)">Add Element</button>
</ul>
</script>
</head>
<body ng-app="Application" ng-controller="jsoncrudctrl">
<ul>
<li ng-repeat="(key,value) in json" ng-include="'node.html'"></li>
</ul>
<pre>{{stringify(json)}}</pre>
</body>
</html>
Одна вещь, которую я хотел бы сделать, это заменить isArrayElement (значение) на isArray (parent), потому что isArrayElement опирается на метаданные, которые я добавил в массив, которые я бы предпочёл не добавлять.
Ответы
Ответ 1
ng-repeat
создает новую область для каждого элемента.
ng-include
также создает новую область.
Когда вы используете ng-include
вместе с ng-repeat
, для каждого элемента создано 2 области:
- Область, созданная
ng-repeat
для текущего элемента. Эта область содержит текущий элемент в итерации.
- Область содержимого, созданная
ng-include
, которая наследует из ng-repeat
созданной области.
Свойство $parent
текущей области действия позволяет вам получить доступ к его родительской области.
По существу, область, созданная ng-include
, пуста, когда вы получаете доступ к value
и key
в node.html
, она фактически получает доступ к унаследованным значениям из родительской области, созданной ng-repeat
.
В node.html доступ к {{value}}
фактически совпадает с {{$parent.value}}
. Поэтому, если вам нужно получить доступ к родительскому элементу текущего элемента, вам нужно сделать еще один шаг, обратившись к {{$parent.$parent.value}}
Этот DEMO прояснит ситуацию:
<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>
Если вам нужно упростить доступ к родительскому элементу, вы можете попробовать инициализировать родителя, используя onload
of ng-include
. Вот так:
На верхнем уровне нет ng-if
= > только 2 уровня глубины
<ul>
<li ng-repeat="(key,value) in json"
ng-include="'node.html'" onload="parent=$parent.$parent"></li>
</ul>
На ваших суб-уровнях существует ng-if
= > 3 уровня:
<ul ng-if="isObject(value)">
<li ng-repeat="(key,value) in value"
ng-include="'node.html'" onload="parent=$parent.$parent.$parent"></li>
</ul>
Внутри шаблона вы можете получить доступ к родительскому элементу, используя parent
, grandparent с помощью parent.parent
и т.д.
DEMO
Ответ 2
Я бы держался подальше от Angular $parent
и всего $scope
создания и наследования безумия. Вам нужно сохранить ссылку на родительский объект, не обнаружив, сколько областей находится между ng-include
и ng-repeat
и жонглирование с помощью $parent.$parent
. Мое предложение состоит в том, чтобы явно сохранять ссылки в значения, которые вас интересуют, просто реализуйте их самостоятельно с помощью настраиваемой логики. Например, если вы хотите выставить значение из родительского ng-repeat для дочернего 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>
Затем в вашем javascript-коде вы можете рекурсивно получить доступ ко всем родителям
function doSomething(value){
parent1 = value._parent;
parent2 = parent1._parent;
}
Тот же код с 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>