Search code examples
typescriptvue.jstreeview

How to add object to another object in a Vue 3 Tree View


I currently have a TreeView component that contains a tree. This tree contains Children that have each have a label, perm, and its own children.

Example of the tree:

App.vue

let tree = ref({
  label: 'org',
  perm: {
    POST: "auth",
    GET: "null"
  },
  children: [
    {
      label: '*',
      perm: {
        POST: "auth",
        GET: "null"
      },
      children: [{
        label: 'repo',
        perm: {
          POST: "null",
          GET: "null"
        },
        children: []
      }]
    },
    {
      label: 'test',
      perm: {
        POST: "auth",
        GET: "auth"
      },
      children: []
    }
  ]
})

I'm currently displaying this in a simple Tree View using this:

TreeView.vue

 <div @click="toggleHideShow()"> 
     {{ label }}
 </div>
 <div class="child" v-if="hideShow">
     <TreeView v-for="child in children" :label="child.label" :perm="child.perm" :children="child.children" />
 </div>

I intend to be able to add children dynamically through this TreeView, so is there a way for me to get the exact Children object onclick?

Right now what I've tried is passing the attributes through the toggleHideShow method:

 <div @click="toggleHideShow(label, perm, children)"> 

and then creating a new Children object, looping through the tree and then comparing each one to find the matching object.

const toggleHideShow = (label: string, perm: Perm, children?: Array<Children>)=> {
    hideShow.value = !hideShow.value
    const newChild: Children = {
        label: label,
        perm: perm,
        children: children
    }

    //do for loop here
}

The problem here is that my tree variable is declared in the App.vue of my project, and I am passing this tree object into my TreeView.

<TreeView :label="tree.label" :perm="tree.perm" :children="tree.children" />

So I have 3 questions:

1: How can I access this tree variable from my TreeView component, and would modifying it also modify the tree variable that is inside my App.vue, and hence dynamically updating the TreeView?

2: Would it be better for me to declare a method inside App.vue that took in the newly created newChild variable as a parameter and then adding it to the tree from there? If so how would I go about creating this method from my TreeView component?

3: Is there any other way for me to know which Children object in the tree I have clicked on? And then how would I access it from the tree variable since it isn't an array?


Solution

  • With recursion, any change made to a child has to be made at parent level. If you're 2 levels deep, you emit an event updating the parent => emits an event upgrading the grand parent => updates the parent => updates the child.

    This happens even when you're 10 levels deep.
    You'd think the biggest problem with this approach is performance. It's not. Vue is quite efficient at change detection and rendering.
    The biggest problem will be debugging. If you start getting errors at some level, it will be difficult to figure out where and what's wrong.

    I suggest a simpler approach:

    • put all your items into a flat array and give them unique identifiers
    • instead of keeping items inside children, only keep their ids

    Now, whenever you update an item, neither its ancestors nor its descendants need to know about the change.

    You can have an item editor which gets the currently selected item and can modify it. Because you don't need to worry about what happens to its children or about broadcasting the change to the top level, everything is more straight forward and testable.

    Working demo: https://codesandbox.io/s/editable-tree-bo6msx?file=/src/App.vue

    To keep things organised, I've extracted the tree related logic into a store, which exports methods to add/remove/edit tree items.

    Another benefit of this approach is it could be easily modified to allow multiple parents for the same item, should you ever need it (the only thing that needs to change is removeChild method, which needs to look for all parents, not for only one).