【问题标题】:AngularJS: traversing nested arraysAngularJS:遍历嵌套数组
【发布时间】:2014-04-29 20:30:34
【问题描述】:

我正在使用具有结构的嵌套数组...

$scope.items = [{attr1: val1, 
  attr2: val2,
  items: [{
     attr1: val1, 
     attr2: val2,
     items: [{
     ... 
     }, ...]
  }, ...]
}, ...];

像这样进入ng-repeatng-include

<div ng-repeat="item in items" ng-include="'/path/to/template.tpl.html'"></div>

template.tpl.html

<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>

现在,在控制器中,我通常想做一些事情,比如

  • 查找项目的父项
  • 查找项目的同级
  • 计算兄弟姐妹数
  • 找出一个项目嵌套了多少层
  • 在嵌套的任何级别插入或删除项目

但我不确定如何优雅地做到这一点。例如,想象一下我想实现fnAddNewItemBelow。我可以解决的两个选项是

遍历作用域

使用 Angular 提供的嵌套范围结构

// 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)
}

但这很丑陋,因为它对结构的假设太多(如果我将 ng-if 放在 &lt;div ng-click... 上,这增加了另一个范围级别......那么我需要parent.$parent.$parent.item.items.push(newItem))。

递归迭代嵌套数组,直到找到 item.id

另一种方法是直接在$scope.items 上操作,因为 Angular 会更新 UI 和与之关联的范围。我可以使用 for 循环通过 $scope.items 递归迭代,并在通过它拥有的某个唯一 ID 定位项目后,在其后插入 newItem

// 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
}

我不喜欢这样,因为每次我想对嵌套数组做某事时,它都需要遍历整个项目树。

还有更优雅的解决方案吗?

【问题讨论】:

    标签: angularjs angularjs-ng-repeat


    【解决方案1】:

    如果您在 ng-repeat 表达式中使用别名 item.items,Angular 将为您跟踪数组结构和层次关系。

    <div ng-repeat="item in items = item.items">
    

    然后,对树的操作可以简单地传入item$indexitems 的数组——无需了解完整的数组结构:

      <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);
    }
    

    要获取兄弟姐妹的数量,可以参考items.length

    <h3>Item #{{$index + 1}} of {{items.length}}</h3>
    

    如果您确实需要从子项目访问父兄弟,您可以为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

    【讨论】:

    • items 技巧很棒,它解决了“兄弟姐妹”问题(添加兄弟姐妹、计算兄弟姐妹、删除兄弟姐妹)。谢谢!而且我已经按照你的方式解决了“儿童”问题。但我看不出您的解决方案如何帮助解决“父母”问题。例如,我如何找到父级,或者找到嵌套级别,或者插入一个项目,比如说,一个“阿姨”?我错过了什么吗?
    • 您可以使用ng-repeat="item in items = (parent = item).items" 保存父级。然后使用ng-init 将父项添加到项目:ng-init="item.parent = parent"
    • 哈!这个解决方案和 gorpacrate 的解决方案一样令人难以置信和疯狂。我现在使用这个,因为它更方便 - Angular 可以管理结构的更改(对象从一个地方移动到另一个地方等)。伟大的工作谢谢! :)
    • 如果有人在items 上使用ng-repeat 过滤器,不要忘记items 在传递到函数调用时是经过PRE 过滤的。我假设相反 => 几个小时的伤害
    • @CaptainFantastic plunker 链接仍然对我有用。也许尝试强制它在旧版本中打开? plnkr.co/edit/xKSwHAUdXcGZcwHTDmiv?plnkr=legacy&p=preview
    【解决方案2】:

    在渲染数据之前,可以做一些准备工作。对您的数据进行一次递归运行以设置级别值以及到每个项目的父项的链接。 使用 LoDash 的数据示例:

    var level = 0;
    _.each($scope.items, function(item){recursive(item, level)});
    
    function recursive(item, level){
        item.level = level;
        _.each(item.items, function(innerItem){
            innerItem.parent = item;
            recursive(innerItem, level+1);
        });
    }
    

    所以现在您可以轻松获取每个项目的父级和兄弟级。

    查找项目的父项 -> item.parent

    找到一个项目的兄弟 -> item.parent.items[i]

    计算兄弟姐妹 -> item.parent.items.length

    找出一个项目嵌套了多少层 -> item.level

    在嵌套的任何级别插入或删除项目(移动操作示例)->

    newParent.items.push(item);
    _.remove(item.parent.items, function(child){return child == item;});
    

    我遇到的这种方法的唯一缺点 - 如果不进行无休止的递归,您将无法轻松克隆整棵树。但是您可以制作不会复制链接的自定义克隆功能。

    【讨论】:

    • 你创建的这个结构很漂亮......也很可怕。我不知道你可以在没有大递归问题的情况下做到这一点。感谢您指出您找到的那个 - 我还不需要克隆我的结构。我将在我的项目中对此进行试验,看看会出现什么其他恶魔。
    • 祝你好运,写下你的想法!用了快一年了,感觉还可以。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    • 2017-05-13
    • 2017-09-08
    • 2021-08-18
    • 2010-12-06
    • 2020-07-25
    • 1970-01-01
    相关资源
    最近更新 更多