1

I'm trying to build a treeview component comporting inputs in order to change my source json.

The binding part seems to work fine but the hide/show action on branches is broken :

HTML :

<div id="app">
  <tree :data="json" :link="json"></tree>

  <p>Outside component :</p>
  <pre>{{json}}</pre>
</div>

JS :

let json = {
  nodeA: {
    nodeA1 : "valueA1",
    nodeA2 : "valueA2"
  },
  nodeB: "valueB",
  nodeC: {
    nodeC1 : "valueC1",
    nodeC2 : "valueC2"
  }
};

Vue.component('tree', {
  name: 'treeview',
  props: [
    'data', 
    'link'
  ],
  template: `<ul>
        <li v-for="(val, key) in data">
            <input type="text" v-if="isLeaf(val)" v-model=link[key]>
            <span @click="toggle">{{key}}</span>
            <tree v-if="!isLeaf(val)" v-show="show" :data="val" :link="link[key]">
            </tree>
        </li>
    </ul>`,
  data: function() {
    return {
      show: false
    };
  },
  methods: {
    isLeaf: function(node) {
      return typeof node != 'object';
    },
    toggle: function() {
      this.show = !this.show;
    }
  }
});

new Vue({
  el: '#app',
  data: {
    json: json
  }
});

https://codepen.io/anon/pen/EZKBwL

As you can see, a click on the first branch ("nodeA") activate both the first and the third branches...

I think the problem comes from the click that occurs on the parent component but I can't find a way to fix my code.

3 Answers 3

0

Your all the branches are hiding/showing together as you are using single variable show to hide and show both of those, You have to use different variable for each on node.

It will be impractical to have as many variables as number of nodes, but you can have a hash like following:

  data: function() {
    return {
      show: {}
    };
  },

and change the toggle method to set the variable for each node by creating a key in this show hash for that node. You can use vm.$set for this which sets a property on an object. If the object is reactive, ensure the property is created as a reactive property and trigger view updates.

toggle: function(node) {
  if(this.show[node]){
    this.$set(this.show, node, false)
  } else {
    this.$set(this.show, node, true)
  }
} 

You need to do corresponding changes in HTML as well, which can be viewed in the working codepen here.

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

2 Comments

Simple but efficient :) In addition, is there a better solution for binding the nested inputs with the source JSON than passing a link as a prop ?
@Pourpre If you are asking about alternative of props, you can use vuex if it suits your requirement, you can look at docs or my answer here or here.
0

It happens because you bind all elements at the same parameter.

To toggle visibility individually for each element you need to store element states at it's own place like object's field or array.

But i guess better solution is toggle class on a target element by click and control visibility by css via class.

Comments

-1

You may need a show field for each node to toggle their visibility separately, in my improved example, I'm using a data structure like this:

{
    "nodeA": {
        "value": {
            "nodeA1": {
                "value": "valueA1",
                "show": false
            },
            "nodeA2": {
                "value": "valueA2",
                "show": false
            }
        },
        "show": true
    },
    "nodeB": {
        "value": "valueB",
        "show": true
    }
}

my template:

<ul>
    <li v-for="(val, key) in data" v-show='val.show'>
        <input type="text" v-if="isLeaf(val)" v-model='link[key].value'>
        <span @click="toggle(val.value)">{{key}}</span>
        <tree v-if="!isLeaf(val)" :data="val.value" :link="val.value">
        </tree>
    </li>
</ul>

methods:

{
    isLeaf: function(node) {
        return typeof node.value != 'object';
    },
    toggle: function(value) {
        for (const nodeName in value) {
            value[nodeName].show = !value[nodeName].show;
        }
    }
}

1 Comment

This solution is functionnal but need a significant change in the source JSON wich is unwanted. Thanks for your proposal.

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.