AngularJS:遍历嵌套数组 [英] AngularJS: traversing nested arrays
问题描述
我正在使用具有结构的嵌套数组...
$scope.items = [{attr1: val1,属性2:val2,项目: [{属性 1: val1,属性2:val2,项目: [{...}, ...]}, ...]}, ...];
它进入一个 ng-repeat
和 ng-include
像这样
<div ng-repeat="item in items" ng-include="'/path/to/template.tpl.html'"></div>
和template.tpl.html
是
{{item.attr1}}<\div><div>{{item.attr2}}<\div><div ng-click="fnAddNewItemBelow(item, $parent)"><\div><div ng-repeat="item.items 中的项目" ng-include="'/path/to/template.tpl.html'"><\div>现在,在控制器中,我通常想做类似
- 查找项目的父项
- 找到一个项目的兄弟
- 计算兄弟姐妹的数量
- 找出一个项目嵌套了多少层
- 在嵌套的任何级别插入或删除项目
但我不确定如何优雅地做到这一点.例如,假设我想实现 fnAddNewItemBelow
.我可以解决的两个选项是
遍历范围
使用 Angular 提供的嵌套作用域结构
//仅伪代码$scope.fnAddNewItemBelow = 函数(项目,父项){var newItem = ...;//在被 ng 单击的项之后添加 newItem 作为兄弟项//parent.$parent 是必要的,因为 ng-include 添加了另一个作用域层(我认为)parent.$parent.item.items.push(newItem);//(可能需要使用.splice,以防item后面有item,//但我保持简单)}
但这很丑陋,因为它对结构假设太多(如果我将 ng-if 放在 <div ng-click...
上会怎样,这增加了另一个范围级别... 那么我需要 parent.$parent.$parent.item.items.push(newItem)
).
递归迭代嵌套数组,直到找到 item.id
另一种方法是直接对 $scope.items
进行操作,因为 Angular 将更新与其关联的 UI 和范围.我可以使用 for 循环通过 $scope.items
递归迭代,并在通过它拥有的某个唯一 id 定位 item 后,在它之后插入 newItem
//仅伪代码$scope.fnAddNewItemBelow = 函数(项目){var newItem = ...;//在被 ng 单击的项之后添加 newItem 作为兄弟项fnSomeFunctionToFindItemAndInsertItemAfterIt(item.id, newItem);}fnSomeFunctionToFindItemAndInsertItemAfterIt (itemId, newItem) {//花哨的递归函数,for 循环遍历每个项目,并调用//当有子项时本身.当它找到带有 itemId 的项目时,它//拼接在newItem之后}
我不喜欢这样,因为每次我想对嵌套数组做某事时,它都需要遍历整个项目树.
有更优雅的解决方案吗?
解决方案 如果你在 ng-repeat
表达式中给 item.items
加上别名,angular 将跟踪数组结构和层次关系.
然后,树上的操作可以简单地传入 item
、$index
或 items
数组 - 无需知道完整的数组结构:
<button ng-click="addItem(item)">添加到我的项目</button><button ng-click="addSiblingItem(items, $index)">添加同级项</button><button ng-click="deleteMe(items, $index)">删除我</button>
js:
$scope.addItem = function(item) {item.items.push({attr1: '我的新 - attr1',attr2: '我的新 - attr2',项目: []});}$scope.addSiblingItem = function(items, position) {items.splice(位置 + 1, 0, {attr1: '兄弟姐妹 - 新的 attr1',attr2: '兄弟姐妹 - 新的 attr2',项目: []});}$scope.deleteMe = 函数(项目,位置){items.splice(位置, 1);}
获取兄弟数量可以参考items.length
:
Item #{{$index + 1}} of {{items.length}}
<小时>
如果您确实需要从子项访问父兄弟项,您可以为 parent = item
添加另一个别名,并使用 ng-init
将其添加到项中:
ng-repeat="item in items = (parent = item).items" ng-init="item.parent = parent"
然后您就可以访问祖父母(parent.parent
)及其items
(父兄弟姐妹).
此外,您可以使用 ng-init
跟踪当前嵌套级别:
ng-init="item.parent = parent; item.level = parent.level + 1"
这是一个工作演示:http://plnkr.co/xKSwHAUdXcGZcwHTDmiv
I am working with a nested array with the structure...
$scope.items = [{attr1: val1,
attr2: val2,
items: [{
attr1: val1,
attr2: val2,
items: [{
...
}, ...]
}, ...]
}, ...];
which goes into an ng-repeat
with ng-include
like this
<div ng-repeat="item in items" ng-include="'/path/to/template.tpl.html'"></div>
and template.tpl.html
is
<div>{{item.attr1}}<\div>
<div>{{item.attr2}}<\div>
<div ng-click="fnAddNewItemBelow(item, $parent)"><\div>
<div ng-repeat="item in item.items" ng-include="'/path/to/template.tpl.html'"><\div>
Now, in the controller, I commonly want to do things like
- find an item's parent
- find an item's sibling
- make counts of siblings
- find out how many levels deep an item is nested
- insert or delete items at any level of the nest
But I'm not sure how to do this elegantly. Eg imagine I wanted to implement fnAddNewItemBelow
. The two options I can work out are
Traverse scopes
Use the nested scopes structure that Angular provides
// pseudo-code only
$scope.fnAddNewItemBelow = function (item, parent) {
var newItem = ...;
// add newItem as a sibling after the item that was ng-clicked
// parent.$parent is necessary because the ng-include adds another scope layer (I think)
parent.$parent.item.items.push(newItem);
// (probably need to use .splice in case there are items after item,
// but I'm keeping it simple)
}
But this is ugly because it assumes too much about the structure (what if I put an ng-if onto the <div ng-click...
, which added another scope level... then I'd need parent.$parent.$parent.item.items.push(newItem)
).
Iterate nested array recursively until item.id is found
The alternative is to operate directly on $scope.items
, since Angular will update UI and scopes associated with it. I can iterate recursively through $scope.items
using for loops and after locating item by some unique id that it has, insert newItem after it
// pseudo-code only
$scope.fnAddNewItemBelow = function (item) {
var newItem = ...;
// add newItem as a sibling after the item that was ng-clicked
fnSomeFunctionToFindItemAndInsertItemAfterIt(item.id, newItem);
}
fnSomeFunctionToFindItemAndInsertItemAfterIt (itemId, newItem) {
// fancy recursive function that for loops through each item, and calls
// itself when there are children items. When it finds item with itemId, it
// splices in the newItem after
}
I don't like this because it requires iterating through the entire items tree every time I want to do something with the nested array.
Are there more elegant solutions?
解决方案 If you alias item.items
in the ng-repeat
expression, angular will keep track of the array structure and hierarchical relationships for you.
<div ng-repeat="item in items = item.items">
Then, operations on the tree can simply pass in the item
, the $index
, or the array of items
- without knowledge of the full array structure:
<button ng-click="addItem(item)">Add to my items</button>
<button ng-click="addSiblingItem(items, $index)">Add a sibling item</button>
<button ng-click="deleteMe(items, $index)">Delete Me</button>
js:
$scope.addItem = function(item) {
item.items.push({
attr1: 'my new - attr1',
attr2: 'my new - attr2',
items: []
});
}
$scope.addSiblingItem = function(items, position) {
items.splice(position + 1, 0, {
attr1: 'sibling - new attr1',
attr2: 'sibling - new attr2',
items: []
});
}
$scope.deleteMe = function(items, position) {
items.splice(position, 1);
}
To get the number of siblings, you can refer to items.length
:
<h3>Item #{{$index + 1}} of {{items.length}}</h3>
If you really need to access the parent siblings from child items, you can add another alias for parent = item
and add it to the item using ng-init
:
ng-repeat="item in items = (parent = item).items" ng-init="item.parent = parent"
Then you have access to the grandparent (parent.parent
) and its items
(the parent siblings).
In addition, you can keep track of the current nest level using ng-init
:
ng-init="item.parent = parent; item.level = parent.level + 1"
Here is a working demo: http://plnkr.co/xKSwHAUdXcGZcwHTDmiv
这篇关于AngularJS:遍历嵌套数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文