代码之家  ›  专栏  ›  技术社区  ›  bernie

向树数据对象添加仅查看属性

  •  3
  • bernie  · 技术社区  · 5 年前

    我正在构建一个树组件,可以在其中移动节点。组件和数据结构与 Tree View example in the Vue.js documentation .

    数据结构如下(删除了不相关的属性):

    [
      {"id": 1, "children": []},
      {"id": 2, "children": []},
      {"id": 3, "children": [
        {"id": 4, "children": [
          {"id": 5, "children": []},
          {"id": 6, "children": []}
        ]}
      ]}
    ]
    

    节点表示可在视图中折叠或展开的“文件夹”。这个 FolderNode 组件如下:

    export default {
      name: 'FolderNode',
      props: {
        node: { type: Object, required: true },
      },
      data: () => ({
        expanded: true,
      }),
    
      methods: {
        toggleExpand() {
          this.expanded = !this.expanded;
        },
      },
    };
    

    模板为:

    <template>
      <li>
        <span class="node-icon">
          <span @click="toggleExpand">[{{ expanded ? '-' : '+' }}]</span>
        </span>
        <span class="node-label">{{ node.id }}</span>
        <ol
          v-if="node.children && node.children.length"
          v-show="expanded"
        >
          <FolderNode
            v-for="child in node.children"
            :key="child.id"
            :node="child"
          />
        </ol>
      </li>
    </template>
    

    这部分工作得很好。我添加了拖放功能来移动树中的节点(为了简单起见,上面没有显示)。当节点被移除并插入到其他位置时,vue.js将实例化新的 折叠节点 组件自动反映更改。这些新 折叠节点 使用默认值创建实例(将节点移动到其他父节点时) expanded 状态到 true . 我希望 :key 属性将在不同的父级之间工作,但它只重用组件(并保留其 扩大 状态)对于同一父级的子级 折叠节点 .

    那么,我如何保存这些文件夹呢? 扩大 移动时的状态(和其他显示状态)?

    树对象将被应用程序的其他部分使用,因此我无法添加 扩大 属性直接指向其节点,这将是最简单的解决方案。此外, 扩大 是严格的“显示状态”,与树数据无关。

    我已经考虑了两种可能的解决方案,但我觉得它们并不真正有吸引力:

    1. 创建一个并行树结构,镜像保持显示状态的数据树。将其传递给文件夹组件,并让它们查询以了解其状态。
    2. 使用映射将数据树的节点映射到状态对象。问题是 maps are not reactive in Vue.js 所以我不得不求助于丑陋的黑客才能让它工作。

    还有其他想法吗?

    2 回复  |  直到 5 年前
        1
  •  1
  •   Harshal Patil    5 年前

    在构建树组件时,我们遇到了类似的问题。我们也有同样的想法。

    最初,我们创建了一个并行数据结构,并借助 Ramda 我们将合并内部数据模型外部树结构。但事实证明这并不十分优雅。而且,树的调解很难实现。

    但是,如果你从另一个角度来看待这种方法,那么 expanded 作为其中的一部分 树节点对象 . 这有多个用例:

    • 有时,您可能希望将第一个节点显示为展开的
    • 在搜索操作过程中,具有特定名称的所有节点都必须按照在大多数代码编辑器中发生的方式进行扩展。
    • 当用作文件资源管理器时,默认情况下必须展开包含特定文件的节点。

    还有很多其他的例子证明它是有用的。我们使用的是typescript,我们只是声明该属性是可选的,比如:

    export interface TreeNode<T extends any> {
        label: string;
        expanded: boolean;
        children: Array<TreeNode<T>>;
    
        isDisabled?: boolean;
    
        // Holds the state if tree/node selected or not
        select?: boolean;
    
        // Unique key which identifies each node
        _id?: string;
    
        // Any other data that needs to be stored
        context?: T;
    }
    

    就API而言,他们不必为这些附加的键操心,因为它们是可选的。因此,支持变得微不足道 拖曳下降 具有扩展状态

        2
  •  0
  •   bernie    5 年前

    另一种可能性是在不使用地图的情况下使用选项2。事实上,地图虽然在语义上是正确的,但却根本不需要!

    “根”组件创建一个“树视图状态”对象,该对象保存 expanded 每个节点。类似:

    const treeViewState = {};
    // Visit each node and create its initial view state object to make it reactive
    walkNodes(tree, (node, level) => {
      treeViewState[node.id] = {
        expanded: level < 1, // Simple logic to only expand the first level by default
      };
    });
    

    然后是“根”组件 provide S treeViewState 通过Vue的注射机制给其子代成分。

    FolderNode 然后通过其节点的唯一ID访问其显示状态。 树视图 对象。