首页 > 解决方案 > 将仅查看属性添加到树数据对象

问题描述

我正在构建一个可以移动节点的树组件。组件和数据结构与Vue.js 文档中的树视图示例中使用的类似

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

[
  {"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 会FolderNode自动实例化新组件以反映更改。这些新FolderNode实例被创建(当节点移动到不同的父节点时),默认expanded状态为true. 我希望该:key属性可以跨不同的父母工作,但它只expanded为同一父母的孩子重用组件(并保持他们的状态)FolderNode

那么,如何在expanded移动文件夹时保持文件夹的状态(和其他显示状态)?

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

我想到了 2 种可能的解决方案,但我觉得这些解决方案并不吸引人:

  1. 创建一个平行树结构,镜像保存显示状态的数据树。将此传递给文件夹组件并让他们查询以了解其状态。
  2. 使用映射将数据树的节点映射到状态对象。问题是地图在 Vue.js 中不是反应式的,所以我不得不求助于丑陋的黑客来使其工作。

还有其他想法吗?

标签: javascriptvue.jsvuejs2

解决方案


我们在构建树组件时遇到了类似的问题。我们有完全相同的想法。

最初,我们创建了一个并行数据结构,并在 的帮助下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 而言,他们不必担心这些额外的密钥,因为它们是可选的。因此,支持具有展开状态的拖放变得微不足道


推荐阅读