Как получить родительский элемент внутри 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>