0

I was folling this tutorial for my own tree view with a recursive component in vuejs. So the input array looks like this:

let tree = {
  label: 'root',
  nodes: [
    {
      label: 'item1',
      nodes: [
        {
          label: 'item1.1'
        },
        {
          label: 'item1.2',
          nodes: [
            {
              label: 'item1.2.1'
            }
          ]
        }
      ]
    }, 
    {
      label: 'item2'  
    }
  ]
}
<template>
  <div>
    ...
    <tree-menu 
      v-for="node in nodes" 
      :nodes="node.nodes" 
      :label="node.label" />
    ...
  </div>
<template

<script>
  export default { 
    props: [ 'label', 'nodes' ],
    name: 'tree-menu'
  }
</script>

So basically a label and a subarray of nodes is passed to a child node. Now I want to update or delete a node (e.g. item1.1), but reflect this change in the outmost array (here tree), because I want to send this updated structure to the server. How can I achive this? If I change the label of a node, this will be rendered in the DOM, but the tree array is not updated.

5
  • 1
    You seem to expect strangers to invest effort into something you have invested none. Unless you consider describing your goal in a Stack Overflow "question" effort. I was talking about research and coding effort. Commented Dec 17, 2020 at 9:37
  • Hey. Sorry, I don't expect a complete solution which I can copy/paste. Treeviews are not an unknown structure and I found some other implementations. In fact I already implemented my own treeview with custom features, but I am stuck here. Of course I searched for a solution by myself, but could not find one. I just expect something like "You have to iterate through the tree again, to build the updated structure.", or "I had the same problem, check this out (link)". Commented Dec 17, 2020 at 10:33
  • You have to emit an event recursively from current level bubbling up all the way up to the top, updating the tree branch which has been modified. Performance wise, you should refrain from mutating the tree until you reach top level, so you don't get a mutation for each level. Only one mutation at top level for each change event. It's unlikely you'll find a useful resource as recursive tree structures are typically very coupled with the implementation. For example, I was never able to reuse a recursive structure, I always need to make a new one for the specific case I'm dealing with. Commented Dec 17, 2020 at 11:08
  • Also, please note my initial comment was not intended as a banter. It was intended as an advice: if you need helpful advice you have to show us what you've tried and what your concrete technical challenge is. In its current form it looks too much like a client request, which, for obvious reasons, is off-topic here. Commented Dec 17, 2020 at 11:10
  • Sorry, I got your point now. Regarding the code, as it is recursively rendered, I need pass the subarray to the tree-menu in order to build the tree structure. Thanks :) I will check this modifier. My problem was, that I don't have an identifier for a node, so how could tree know, which label for which node was changed. Commented Dec 17, 2020 at 16:46

1 Answer 1

1

Here's how you can use the .sync modifier to update recursively:

Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('tree-node', {
  template: `
<div style="margin-left: 5px;">
  <input :value="label"
         type="text"
         @input="$emit('update:label', $event.target.value)" />
  <tree-node v-for="(node, key) in nodes"
             :key="key"
             v-bind.sync="node" />
</div>
`,
  props: ['label', 'nodes']
});

let tree = {
      label: 'root',
      nodes: [{
          label: 'item 1',
          nodes: [
            { label: 'item 1.1' },
            { label: 'item 1.2', 
              nodes: [
                { label: 'item 1.2.1' }
              ]
            }
          ]
        },
        { label: 'item 2' }
      ]
    };

new Vue({
  el: '#app',
  data: () => ({
    tree
  })
})
#app {
  display: flex;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<div id="app">
  <div>
    <tree-node v-bind.sync="tree" />
  </div>
  <pre v-html="tree" />
</div>

v-bind.sync="node" is shorthand for :label.sync="node.label" :nodes.sync="node.nodes". v-bind unwraps all object members as attributes of the tag, resulting in props for the component.

The other half of the solution is replacing v-model on the input with :value + an $emit('update:propName', $event.target.value) call on @input which updates the .sync-ed property in the parent. To conceptualize it, it's a DIY v-model exposed by Vue so it could be customized (you decide when to call the update and what to update with). You can replace the <input> with any other type of input, depending on what you're binding/modifying (checkboxes, textarea, select, or any fancier input wrapper your framework might feature). Depending on type of input you'll want to customize the listener: @change, @someCustomEvent, etc...

.sync makes everything reactive at each individual level. Since everything is :key-ed, no re-rendering actually happens (Vue only re-renders DOM elements which actually changed). If that wasn't the case, the input would lose focus upon re-rendering.

The update principle is: instead of making the change at child level you update the parent property which, through v-bind, sends it back to the child.

It's the same exact principle used by Vuex. Rather than changing some local prop you call a store mutation which comes back through getters and modifies the local value but it happens for any component using that store data, not just for current one.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for the detailed explanation! :) I will check this out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.