You do not necessarily need to keep a folder associated with your state. A more practical option would be to send to all the elements the id
of the folder that has been asked to be opened (by $emit
) and make a check to know if the current folder is open to update its CSS class.
Example
In each folder, we put a conditional to see when to add the class open
(for example):
<li
class="folder"
:class="{open: this.folder.id === open}"
@click="openFolder"
>
</li>
When you click on each folder, the id of that folder is sent to the parent:
openFolder() {
this.$emit('open', this.folder.id);
}
Each folder will have the properties folder
and open
. The latter represents the id of the folder that has asked to open.
props: {
open: {
type: Number,
required: true,
},
folder: {
type: Object,
required: true
},
},
And finally, the parent, upon receiving notification that a folder wants to open, updates its own property folderOpen
:
methods: {
onFolderOpen(id) {
this.folderOpen = id;
}
},
which is passed as property to each folder:
<ul class="folders">
<folder
v-if="ready"
v-for="folder in folders"
:folder="folder"
:open="folderOpen"
@open="onFolderOpen"
/>
</ul>
Full code
Vue.component('folder', {
template: '
<li
class="folder"
@click="openFolder"
>
<i
class="material-icons"
v-if="open !== folder.id"
>
folder
</i>
<i
class="material-icons"
v-if="open === folder.id"
>
folder_open
</i>
<label>{{ folder.name }}</label>
</li>
',
props: {
open: {
type: Number,
required: true,
},
folder: {
type: Object,
required: true
},
},
methods: {
openFolder() {
this.$emit('open', this.folder.id);
}
},
});
const app = new Vue({
data: () => ({
folders: [],
folderOpen: -1,
ready: false,
}),
methods: {
onFolderOpen(id) {
this.folderOpen = id;
}
},
created() {
this.folders = [{
id: 0,
name: 'Proyectos',
},
{
id: 1,
name: 'Trabajo',
},
{
id: 2,
name: 'Hacking',
},
{
id: 3,
name: 'StackOverflow'
},
];
this.ready = true;
},
});
app.$mount('#app');
ul.folders {
list-style: none;
}
ul.folders li {
align-items: center;
color: #555;
cursor: pointer;
display: flex;
font-family: 'Open Sans', sans-serif;
font-size: 15px;
height: 40px;
}
ul.folders li i {
margin: 0 10px 0 0;
pointer-events: none;
}
ul.folders li label {
font-weight: 600;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"/>
<div id="app">
<ul class="folders">
<folder v-if="ready" v-for="folder in folders" :folder="folder" :open="folderOpen" @open="onFolderOpen" />
</ul>
</div>