AngularJS - 如何制作一个可拖动的树?

时间:2013-01-12 22:25:54

标签: drag-and-drop tree angularjs

我想创建一个树状结构,用户可以拖放树叶。我的起点如下:

HTML

<div ng:controller="controller">
  <ul ui-sortable ng-model="items" ui-options="{connectWith: '.item'}" class="item">
    <li ng-repeat="item in items" class="item">
      {{ item.name }}
      <ul ui-sortable ng-model="item.children" ui-options="{connectWith: '.item'}" class="item">
        <li ng-repeat="item in item.children" class="item">{{ item.name }}</li>
      </ul>
    </li>
  </ul>

  <pre>{{ items | json }}</pre>
</div>

<script src="http://code.angularjs.org/1.0.2/angular.min.js"></script>
<script src="https://raw.github.com/angular-ui/angular-ui/master/build/angular-ui.min.js"></script>

的CoffeeScript

myapp = angular.module 'myapp', ['ui']

myapp.controller 'controller', ($scope) ->

    $scope.items = [
      {id: 1, name: 'Item 1', children: [
        {id: 5, name: 'SubItem 1.1', children: [
          {id: 11, name: 'SubItem 1.1.1', children: []},
          {id: 12, name: 'SubItem 1.1.2', children: []}
        ]},
        {id: 6, name: 'SubItem 1.2', children: []}
      ]},
      {id: 2, name: 'Item 2', children: [
        {id: 7, name: 'SubItem 2.1', children: []},
        {id: 8, name: 'SubItem 2.2', children: []}
        {id: 9, name: 'SubItem 2.3', children: []}
      ]},
      {id: 3, name: 'Item 3', children: [
        {id: 10, name: 'SubItem 3.1', children: []}
      ]}
    ]

angular.bootstrap document, ['myapp']

代码也在这个JSFiddle中:http://jsfiddle.net/bESrf/1/

在我的“真实”代码中,我将第二个<ul>提取到一个模板并递归渲染,而不是只有一个级别的子代码,这很好,但我找不到办法它在JSFiddle中。

递归渲染它的最佳方法是什么?仍然允许拖放,这会改变由ng-model表示的对象和子对象的数组?

3 个答案:

答案 0 :(得分:20)

看一下这个例子:http://jsfiddle.net/furf/EJGHX/

我刚刚完成了这个解决方案,因此尚未正确记录,但您应该能够将其解决。

你需要使用一些东西:

  1. ezTree指令 - 渲染树
  2. Manuele J Sarfatti's nestedSortable plugin for jQuery UI
  3. (可选)uiNestedSortable指令 - 从模板启用nestedSortable。
  4. 用于更新模型的控制器代码 - 请参阅$scope.update
  5. 使用ezTree指令

    给定递归数据结构:

    $scope.data = {
      children: [{
        text: 'I want to create a tree like structure...',
        children: [{
          text: 'Take a look at this example...',
          children: []
        }]
      }]
    };
    

    此模板将构建树:

    <ol>
      <li ez-tree="child in data.children at ol">
        <div>{{item.text}}</div>
        <ol></ol>
      </li>
    </ol>
    

    ez-tree表达式应写为item in collection at selector,其中item是迭代子项(ala ng-repeat),collection是根级别集合,并且selector是模板内节点的CSS选择器,其中指令应该递归。集合的终端属性的名称(在本例中为children)将用于递归树,在本例中为child.children这可以重写为可配置的,但我会将其作为读者的练习。

    使用uiNestedSortable指令

    <ol ui-nested-sortable="{ listType: 'ol', items: 'li', doNotClear: true }"
      ui-nested-sortable-stop="update($event, $ui)">
    </ol>
    

    ui-nested-sortable属性应包含nestedSortable插件的JSON配置。该插件要求您指定listTypeitems。我的解决方案要求doNotCleartrue。使用ui-nested-sortable-*eventName*为事件分配回调。我的指令为回调提供了可选的$ event和$ ui参数。有关其他选项,请参阅nestedSortable的文档。

    更新模型

    这种猫的皮肤有多种方法。这是我的。在stop事件中,它提取元素范围的子属性以确定移动了哪个对象,元素父级范围的子属性以确定对象的目标,以及元素的位置以确定对象的位置在目的地。然后它遍历数据结构并将对象从其原始位置移除并将其插入新位置。

    $scope.update = function (event, ui) {
    
      var root = event.target,
        item = ui.item,
        parent = item.parent(),
        target = (parent[0] === root) ? $scope.data : parent.scope().child,
        child = item.scope().child,
        index = item.index();
    
      target.children || (target.children = []);
    
      function walk(target, child) {
        var children = target.children,
          i;
        if (children) {
          i = children.length;
          while (i--) {
            if (children[i] === child) {
              return children.splice(i, 1);
            } else {
              walk(children[i], child)
            }
          }
        }
      }
      walk($scope.data, child);
    
      target.children.splice(index, 0, child);
    };
    

答案 1 :(得分:6)

使用furf轻微编辑小提琴,使其在IE中运行。

IE在第二个参数为null时在insertNode上给出错误,所以在这种情况下使用appendNode代替。

http://jsfiddle.net/michieljoris/VmtfR/

if (!cursor) parentNode.appendChild(cached.element);
else parentNode.insertBefore(cached.element, cursor);

嵌套可排序插件在js中内联,因为IE在github中包含MIME类型不匹配。

答案 2 :(得分:6)

尝试使用Angular-NestedSortable,它是一个Angularjs插件,可以对嵌套列表进行排序并绑定数据,而不需要依赖于jQuery。 https://github.com/jimliu/Angular-NestedSortable