I have a problem with this code: I'm trying to make a tree viewer from a object and it work great until when a value is exactly the same as another value it clone de key and it replace the same key where the value is the same.
On CodePen: https://codepen.io/onigetoc/pen/rNPeQag?editors=1010
This object:
[
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"description": "delectus aut autem"
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"description": "et porro tempora"
},
]
Give me in my tree view that:
[-]
userId:1
userId:1
title:delectus aut autem
title:delectus aut autem
[-]
userId:1
id:2
title:quis ut nam facilis et officia qui
description:et porro tempora
We can see that the keys userId and title are duplicated because they have the same values. The second part is ok because there's no identical values.
HTML part:
<!-- item template -->
<script type="text/x-template" id="item-template">
<li>
<span :class="{bold: isFolder}" @click="toggle">
<span class="key" v-if="!isFolder" @click="toggleKey">{{ key }}:</span>
<span class="value" v-if="!isFolder">{{ value }}</span>
<span v-if="isFolder">[{{ isOpen ? '-' : '+' }}]</span>
</span>
<ul v-show="isOpen" v-if="isFolder">
<tree-item
class="item"
v-for="(child, index) in item"
:key="index"
:item="child"
></tree-item>
</ul>
</li>
</script>
<p>(You can double click on an item to turn it into a folder.)</p>
<!-- the demo root element -->
<ul class="view-list" id="connect-list">
<tree-item class="item" :item="treeData" @make-folder="makeFolder" @add-item="addItem"></tree-item>
</ul>
Javascript part:
<!-- demo data -->
// https://jsonplaceholder.typicode.com/users
var treeData = [
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"description": "delectus aut autem"
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"description": "et porro tempora"
},
];
const app = Vue.createApp({
data: function() {
return {
treeData: treeData
}
},
methods: {
makeFolder: function(item) {
Vue.set(item, 'newFolder', {});
this.addItem(item.newFolder);
},
addItem: function(item) {
Vue.set(item, 'newItem', 'new stuff');
}
}
})
app.component("tree-item", {
template: '#item-template',
props: {
item: Object
},
data: function() {
return {
isOpen: true // default was false
};
},
computed: {
isFolder: function() {
return typeof this.item === 'object' && this.item !== null;
},
key: function() {
if (typeof this.item === 'object' && this.item !== null) {
return '';
} else {
return Object.keys(this.$parent.item).find(key => this.$parent.item[key] === this.item);
}
},
value: function() {
if (typeof this.item === 'object' && this.item !== null) {
return '';
} else {
return this.item;
}
}
},
methods: {
toggle: function() {
if (this.isFolder) {
this.isOpen = !this.isOpen;
}
},
toggleKey: function(event) {
event.target.classList.toggle("bgcolor");
}
}
})
app.mount('#connect-list')
The issue is that Object.keys(this.$parent.item).find(key => this.$parent.item[key] === this.item);
will find the first key with a matching value
Simple fix really, you need to pass the "index" as a prop (since the index in an object is a string, and item
is an Object in tree-item
)
I use name
as the prop, but I don't really like that :p can't think of a better name for this prop - but, that's not really something to worry about
<tree-item
class="item"
v-for="(child, index) in item"
:key="index"
:name="index"
:item="child"
></tree-item>
note: keep the :key="index"
as that is vital for the v-for
and return this.name
in the key:
computed
key: function() {
if (typeof this.item === 'object' && this.item !== null) {
return '';
} else {
return this.name;
}
},