Suppose I have a parent checkbox and under this I have four child checkboxes. For example-
-Animals
-Cat
-Dog
-Squirrel
-Lion
If I select the parent, then all the childs will be selected. If I deselect all the child, the parent will be unselected. If I select only one child then also the parent will be selected. How to implement this in vue.js ?
json:
{
"id": 266,
"parent_id": 0,
"menu_name": "Server && DB Info",
"menu_url": "#",
"menu_icon": "fa fa-database",
"order_by": 6,
"has_child": 1,
"child": [
{
"id": 269,
"parent_id": 266,
"menu_name": "Client",
"menu_url": "/server_and_db_info/client",
"menu_icon": null,
"order_by": 7,
"has_child": 0,
"child": []
},
{
"id": 268,
"parent_id": 266,
"menu_name": "VPN",
"menu_url": "/server_and_db_info/vpn",
"menu_icon": null,
"order_by": 8,
"has_child": 0,
"child": []
},
{
"id": 262,
"parent_id": 266,
"menu_name": "Server",
"menu_url": "/server_and_db_info/server",
"menu_icon": "",
"order_by": 9,
"has_child": 0,
"child": []
},
{
"id": 267,
"parent_id": 266,
"menu_name": "Database",
"menu_url": "/server_and_db_info/database",
"menu_icon": null,
"order_by": 10,
"has_child": 0,
"child": []
},
{
"id": 271,
"parent_id": 266,
"menu_name": "Domain",
"menu_url": "/server_and_db_info/domain",
"menu_icon": null,
"order_by": 15,
"has_child": 0,
"child": []
}
]
}
Code:
<div
class="menu-padding padding-top"
v-for="(data, index) in getAllMenu"
:key="index"
>
<input
type="checkbox"
v-model="selectedMenu"
:value="data.id"
@change="select(data.id, index)"
/>
<label>{{ data.menu_name }}</label>
<div
class="padding-left"
v-if="data.child.length > 0"
>
<div
v-for="(data_child, index) in data.child"
:key="index"
>
<input
type="checkbox"
v-model="selectedMenu"
:value="data_child.id"
@change="selectChild(data.id, data_child.id)"
/>
<label
>{{ data_child.menu_name }}
{{ data_child.id }}</label
>
</div>
</div>
</div>
CodePudding user response:
Try like following snippet:
new Vue({
el: '#demo',
data() {
return {
getAllMenu: [
{"id": 256, "menu_name": "Server", "child": []},
{"id": 266, "menu_name": "Server && DB Info", "child": [
{"id": 269, "menu_name": "Client",},
{"id": 268, "menu_name": "VPN",},
{"id": 262, "menu_name": "Server",},
{"id": 267, "menu_name": "Database",},
{"id": 271, "menu_name": "Domain",}
]},
{"id": 286, "menu_name": "Info", "child": [
{"id": 289, "menu_name": "C",},
{"id": 288, "menu_name": "V",},
{"id": 282, "menu_name": "S",},
{"id": 287, "menu_name": "D",},
]},
],
selectedMenu: []
}
},
methods: {
selChild(id) {
return this.selectedMenu.find(s => s.child.find(c => c.id === id))
},
selParent(id) {
return this.selectedMenu.find(s => s.id === id)
},
select(data) {
if(this.selectedMenu.find(s => s.id === data.id)) {
this.selectedMenu = this.selectedMenu.filter(s => s.id !== data.id)
} else {
this.selectedMenu.push(data)
}
},
selectChild(id, childId) {
if (this.selectedMenu.length) {
let par = this.selectedMenu.find(s => s.id === id)
let parent = {...par}
let idx = this.getAllMenu.findIndex(f => f.id === id)
if (par && parent.child.find(p => p.id === childId)) {
if(parent.child.length > 1) {
parent.child = parent.child.filter(c => c.id !== childId)
let i = this.selectedMenu.findIndex(f => f.id === parent.id)
this.selectedMenu = this.selectedMenu.filter(m => m.id !== parent.id)
this.selectedMenu.push(parent)
} else {
this.selectedMenu = this.selectedMenu.filter(s => s.id !== id)
}
} else {
let ch = [...this.getAllMenu]
if(par) {
ch = ch[idx].child.filter(f => f.id === childId)
parent.child.push(...ch)
} else {
par = this.getAllMenu.find(f => f.id === id)
let parent = {...par}
parent.child = parent.child.filter(f => f.id === childId)
this.selectedMenu.push(parent)
}
}
} else {
let par = this.getAllMenu.find(s => s.id === id)
let parent = {...par}
parent.child = parent.child.filter(c => c.id === childId)
this.selectedMenu.push(parent)
}
}
}
})
Vue.config.productionTip = false
Vue.config.devtools = false
.padding-left {
padding-left: 20px;
}
ul {
list-style: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<ul>
<li
class="menu-padding padding-top"
v-for="(data, index) in getAllMenu"
:key="data.id"
>
<input type="checkbox" :value="data.id" @change="select(data)" :checked="selParent(data.id)" />
<label>{{ data.menu_name }}</label>
<ul class="padding-left" >
<li v-for="data_child in data.child" :key="data_child.id">
<input type="checkbox" :value="data_child.id" @change="selectChild(data.id, data_child.id)" :checked="selChild(data_child.id)" />
<label>{{ data_child.menu_name }} - {{ data_child.id }}</label>
</li>
</ul>
</li>
</ul>
<p>{{ selectedMenu }}</p>
</div>
CodePudding user response:
The problem is, that you provided the same v-model
to all of your checkboxes without reference for each of it. That means, that v-model="selectedMenu"
get´s overwritten by every new value that is passed by any element that uses this v-model
.
As your data is nested, you should pre-build the v-model
for this. Define a object like this:
myVModel: {}
This function will check, if your provided data, in this case named myObject
, has childs, if yes, it will expand your v-model
myModel
for each child:
preBuildVModel() {
Object.keys(this.myObject).forEach(parentId => {
if(this.myObject[parentId].has_child === 1) {
Object.assign(this.myVModel, {[this.myObject[parentId].id]: {
'parent': null,
'childs': {}
}
})
Object.keys(this.myObject[parentId].child).forEach(childId => {
console.log(this.myObject[parentId].child[childId].id)
Object.assign(this.myVModel[this.myObject[parentId].id].childs,
{[this.myObject[parentId].child[childId].id]: null
})
});
}
else {
Object.assign(this.myVModel, {[this.myObject[parentId].id]: {
'parent': null
}
})
}
});
}
I added another parent to your provided data, as I asume there are multiple parents and also the case, that there are no parents. In my example there is another parent with id
100 and no children. The myVModel
would look like this:
{
"100":{
"parent":null
},
"266":{
"parent":null,
"childs":{
"262":null,
"267":null,
"268":null,
"269":null,
"271":null
}
}
}
So you need to fix your hook now, as your v-model
needs to be set correctly. I will set them to my examples, just change them to the declared names you used:
<div class="menu-padding padding-top" v-for="(data, index) in myObject" :key="index">
<input
type="checkbox"
v-model="myVModel[data.id].parent"
:value="data.id"
/>
<label>{{ data.menu_name }}</label>
<div class="padding-left" v-if="data.child.length > 0">
<div v-for="(data_child, index) in data.child" :key="index">
<input
type="checkbox"
v-model="myVModel[data.id].childs[data_child.id]"
:value="data_child.id"
/>
<label>{{ data_child.menu_name }} {{ data_child.id }}</label>
</div>
</div>
</div>
I deleted your @change
as you not provided them.
CodePudding user response:
I have done it like this from another source and it's working:
<div
class="menu-padding padding-top"
v-for="(data, index) in getAllMenu"
:key="index"
>
<input
type="checkbox"
v-model="selectedMenu"
:value="data.id"
@change="select(data.id, index)"
/>
<label>{{ data.menu_name }}</label>
<div
class="padding-left"
v-if="data.child.length > 0"
>
<div
v-for="(data_child, index_child) in data.child"
:key="index_child"
>
<input
type="checkbox"
v-model="selectedMenu"
:value="data_child.id"
@change="
selectChild(data.id, data_child.id, index)
"
/>
<label>{{ data_child.menu_name }}</label>
</div>
</div>
</div>
And the functions:
selectChild(id, child_id, index) {
this.$nextTick(function () {
const self = this;
if (this.selectedMenu.includes(child_id)) {
this.selectedMenu.push(id);
this.selectedMenu = [...new Set(this.selectedMenu)];
// console.log(this.selectedMenu);
} else {
var arrData = false;
this.getAllMenu[index].child.forEach((data) => {
console.log(data.id);
if (self.selectedMenu.includes(data.id)) {
arrData = true;
}
});
if (arrData === false) {
console.log(this.getAllMenu[index].id);
this.selectedMenu = this.arrayRemove(
this.selectedMenu,
this.getAllMenu[index].id
);
}
// this.selectedMenu.pop(id)
this.selectedMenu = [...new Set(this.selectedMenu)];
}
});
},
select(id, index) {
this.$nextTick(function () {
if (this.selectedMenu.includes(id)) {
if (this.getAllMenu[index].child.length > 0) {
for (let i = 0; i < this.getAllMenu[index].child.length; i ) {
this.selectedMenu.push(this.getAllMenu[index].child[i].id);
}
console.log(this.selectedMenu);
}
} else {
for (let i = 0; i < this.getAllMenu[index].child.length; i ) {
this.selectedMenu = this.arrayRemove(
this.selectedMenu,
this.getAllMenu[index].child[i].id
);
}
console.log(this.selectedMenu);
}
});
//console.log(this.selectedMenu)
},
arrayRemove: function (arr, value) {
return arr.filter(function (ele) {
return ele != value;
});
},