I'm building a tree component where nodes can be moved around. The component and data structure are similar to the one used in the Tree View example in the Vue.js documentation.
The data structure looks like this (irrelevant properties removed):
[
{"id": 1, "children": []},
{"id": 2, "children": []},
{"id": 3, "children": [
{"id": 4, "children": [
{"id": 5, "children": []},
{"id": 6, "children": []}
]}
]}
]
The nodes represent "folders" that can be folded or expanded in the view. The FolderNode component looks like this:
export default {
name: 'FolderNode',
props: {
node: { type: Object, required: true },
},
data: () => ({
expanded: true,
}),
methods: {
toggleExpand() {
this.expanded = !this.expanded;
},
},
};
The template is:
<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>
This part works fine. I added drag and drop functionality to move nodes around (not shown above for simplicity) in the tree. When nodes are removed and inserted elsewhere, Vue.js instantiates new FolderNode components automatically to reflect the changes. These new FolderNode instances are created (when a node is moved to a different parent) with the default expanded state to true. I was hoping the :key property would work across different parents, but it only reuses components (and keeps their expanded state) for children of the same parent FolderNode.
So, how can I keep the folders' expanded state (and other display state) when they are moved?
The tree object will be used by other parts of the application so I can't add an expanded property directly to its nodes which would be the easiest solution. Besides, expanded is strictly "display state" and has nothing to do with the tree data.
I've thought of 2 possible solutions which I don't find really appealing:
- Create a parallel tree structure mirroring the data tree that holds the display state. Pass this to the folder components and have them query that to know their state.
- Use a map to map nodes of the data tree to a state object. The problem is that maps are not reactive in Vue.js so I'd have to resort to ugly hacks to make it work.
Any other ideas?
node.idis a:key? It doesn't change while moving node?:key="child.id"refers to anode.idpropertyexpandedis not stored innodeobject. Why, though?