Recursion AngularJS指令将JSON树呈现为缩进表

Recursion AngularJS指令将JSON树呈现为缩进表,recursion,angularjs-directive,flatten,treetable,Recursion,Angularjs Directive,Flatten,Treetable,我想以角度渲染多列树(即树表或树栅格)。我的数据是一棵任意深度的树,如下所示: [ {name: "node 1", type: "folder", children: {[ {name:"node 1-1", type:file}, {name:"node 1-2", type:file}, {name:"node 1-3", type:file},

我想以角度渲染多列树(即树表或树栅格)。我的数据是一棵任意深度的树,如下所示:

[
    {name: "node 1",
     type: "folder",
     children: {[
                {name:"node 1-1", type:file},
                {name:"node 1-2", type:file},
                {name:"node 1-3", type:file},
                ]}
    },
    ...
    ...
]
我见过使用递归模板在ul/li树中呈现的示例,但我需要多个列,所以我想我需要一个表。到目前为止,我提出的用于呈现上述数据结构的最佳HTML如下所示:

<tr>
    <td>
    node 1
    </td>
    <td>
    folder
    </td>
</tr>
<tr>
    <td>
    <span class="treetable-padding"/>
    node 1-1
    </td>
    <td>
    file
    </td>
</tr>
...
...

节点1
文件夹
节点1-1
文件
...
...
因此,换句话说,它只是一个平面表,其中树的深度用许多span元素表示,这些元素的数量等于层次结构中的那个级别

一般来说,我对html/AngularJS/js不太在行,所以我不知道这是否是呈现数据的最佳方式,但它看起来确实是我想要的方式

我的问题是,使用AngularJS的最佳方法是什么?我知道我可以在JavaScript中构建这样一个完整的html块,然后将其包装到一个自定义指令中,但我正在寻找一个更符合Angular精神的解决方案,这样我就可以利用数据绑定等。我最终希望单元格是可编辑的,所以我不想做任何会使数据绑定混乱的事情

我考虑编写一些代码,将我的树展平为一个对象列表,其中显式包含一个“depth”属性。也许我可以用ng重复一下。但我想也许有一种更干净的方法可以使用一个或多个自定义指令


谢谢

我现在有了一些有效的方法。它以我在对上述问题的评论中描述的方式工作。简单回顾一下,我使用了两个自定义指令,它们都基于ng repeat。第一种方法构建一个平面表结构,在class属性中使用文本值来指示深度。第二个指令使用该深度信息将padding元素重复适当的次数。两个中继器都使用转置来最小化对html的限制/依赖。唯一需要注意的是,第二个中继器需要深度信息位于DOM中其祖先的某个位置

下面是生成我在问题中描述的结构的树表时HTML模板的外观:

<table class="treetable" style="width: 40%;">
  <tr class="treetable-node" my-repeat="item in things"
  my-repeat-children="children">
<td>
  <span my-repeat-padding class="treetable-node-indent"></span>
  {{ item.name }}
</td>
<td>
  {{ item.type }}
</td>
  </tr>
</table>

{{item.name}
{{item.type}
我的自定义中继器基于示例。我的不同之处在于,您可以指定一个“children”属性,指令逻辑将检查每个项上的该属性,如果该属性存在,则递归地下降到该属性中——尽管它将所有新元素附加到同一个父级,给了我平面结构

它可能效率很低,并且可能需要一些优化——就像我提供的链接中的代码一样,它会在发生更改时重建所有内容

myApp.directive("myRepeat", function($parse) {
    return {
    restrict: "A",
    replace: true,
    transclude: "element",
    link: function (scope, element, attrs, controller, transclude) {
        match = attrs.myRepeat.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/);
        itemName = match[1];
        collectionName = match[2];
        childCollectionName = attrs["myRepeatChildren"];
        parentElement = element.parent();
        elements = [];

        scope.$watchCollection(collectionName, function(collection) {
        var i, block, childScope;
        if (elements.length > 0) {
            for(i=0; i<elements.length; i++) {
            elements[i].el.remove();
            elements[i].scope.$destroy();
            };
            elements = [];
        }

        var buildHtml = function(parent, itemList, depth=0) {
            for (var i=0; i<itemList.length; i++) {
            childScope = scope.$new();
            childScope[itemName] = itemList[i];

            transclude(childScope, function(clone) {
                parentElement.append(clone);
                block = {};
                block.el = clone;
                block.scope = childScope;
                block.el.addClass("depth-" + depth);
                elements.push(block);
            });

            /*Recurse if this item has children, 
              adding the sub-elements to the same
              parent so we end up with a flat list*/
            if(childCollectionName in itemList[i]) {
                buildHtml(parentElement,
                      itemList[i][childCollectionName],
                      depth+1);
            }

            }
        }
        for (i=0; i<collection.length; i++) {
            buildHtml(parentElement, collection);
        }

        });
    }
    };
});
myApp.directive(“myRepeat”),函数($parse){
返回{
限制:“A”,
替换:正确,
转移:“元素”,
链接:功能(范围、元素、属性、控制器、转置){
match=attrs.myRepeat.match(/^\s*(.+)\s+in\s+(.*)\s*(\s+track\s+by\s+(.+)\s*)?$/);
itemName=匹配[1];
collectionName=匹配[2];
childCollectionName=attrs[“myRepeatChildren”];
parentElement=element.parent();
元素=[];
作用域$watchCollection(collectionName,函数(collection){
变量i、块、子镜;
如果(elements.length>0){

出于(i=0;i这需要是一个表的任何原因?您可以尝试扩展一个类似于允许编辑的指令。我需要多个列。为了简洁起见,我没有在示例中显示它们。您希望通过额外的列实现什么?:)用于编辑/删除/复制的按钮?如果soI希望其他列始终可见,您可能可以使用ui popover或modal来管理这些按钮。我将使用它们进行编辑/删除等操作,但也会在其中显示数据。在考虑/阅读更多内容后,我现在想我有了一个如何执行此操作的计划。我需要的是像ng repeat这样的自定义转发器,但区别在于它将递归地遵循“item in items”表达式,并构建一个平面表,其中每行包含一个“depth”然后希望我可以在每一行中使用另一个嵌套的线性中继器,它使用depth属性来创建填充跨距。在我真正完成之前,我还有一些学习要做。。。
myApp.directive("myRepeatPadding", function($parse) {
    return {
    restrict: "A",
    replace: true,
    transclude: "element",
    terminal: true,
    link: function (scope, element, attrs, controller, transclude) {
        var getDepth = function(element) {
        classes = element.attr("class");
        if (classes) {
            match = classes.match(/depth-([\d+])/);
            if(match.length > 0) {
            return match[1];
            } else {
            return getDepth(element.parent());
            }
        } else {
            return getDepth(element.parent());
        }


        }
        depth = getDepth(element);
        for (var i=0; i<depth; i++) {
        transclude(scope, function(clone) {
            element.parent().prepend(clone);
            block = {};
            block.el = clone;
            block.scope = scope;
            elements.push(block);
        });
        }
    }
    };
});