So I have a nested table and i want the checkboxes in the header of the table to automatically check all the checkbox in its child
This is my vue code:
// TEMPLATE
<table class="table">
<thead>
<tr>
<th><input type="checkbox" value="row-all" v-model="selectAllCheckbox"></th> --> CHECKBOX IN HEADER OF PARENT TABLE
...
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" :value="`row-${row.id}`" v-model="checkedRows"></td> --> CHECKBOX IN BODY OF PARENT TABLE
...
</tr>
<tr>
<table>
<thead>
<tr>
<th><input type="checkbox" :value="`child-${row.id}`" v-model="selectAllCheckbox"></th> --> CHECKBOX IN HEADER OF CHILD TABLE
...
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" :value="`child-${row.id}-row-${index}`" v-model="checkedRows"></td> --> CHECKBOX IN BODY OF CHILD TABLE
</tr>
</tbody>
</table>
</tr>
</tbody>
</table>
// SCRIPT
const selectAllCheckbox = ref(new Set())
const checkedRows = ref(new Set())
watch(() => selectAllCheckbox.value, () => {
if (selectAllCheckbox.value.has('row-all')) {
for (const key in shownData.value) {
checkedRows.value.add(`row-${shownData.value[key].id}`)
}
} else {
for (const key in shownData.value) {
checkedRows.value.delete(`row-${shownData.value[key].id}`)
}
}
for (const key in shownData.value) {
if (selectAllCheckbox.value.has(`child-${shownData.value[key].id}`)) {
for (let index = 0; index < shownData.value[key].product_entries.length; index++) {
checkedRows.value.add(`child-${shownData.value[key].id}-row-${index}`)
}
} else {
for (let index = 0; index < shownData.value[key].product_entries.length; index++) {
checkedRows.value.delete(`child-${shownData.value[key].id}-row-${index}`)
}
}
}
})
watch(() => checkedRows.value, () => {
let condition = true
for (const key in shownData.value) {
if (!checkedRows.value.has(`row-${shownData.value[key].id}`)) {
condition = false
}
}
if (condition) {
selectAllCheckbox.value.add('row-all')
} else {
selectAllCheckbox.value.delete('row-all')
}
for (const key in shownData.value) {
condition = true
for (let index = 0; index < shownData.value[key].product_entries.length; index++) {
if (!checkedRows.value.has(`child-${shownData.value[key].id}-row-${index}`)) {
condition = false
}
}
if (condition && shownData.value[key].product_entries.length > 0) {
selectAllCheckbox.value.add(`child-${shownData.value[key].id}`)
} else {
selectAllCheckbox.value.delete(`child-${shownData.value[key].id}`)
}
}
})
My main question is about the checkboxes. Now the code above works fine, my question is:
Basically my recommendation is always decompose everything to smaller components.
You can use a combination of :checked
and @input
and computed
(for the all selected checkboxes):
<script setup>
import { reactive, computed } from 'vue'
const props = defineProps({items:Array});
const selectedRows = reactive(new Set);
const allSelected = computed({
get(){ return selectedRows.size === props.items.length; },
set(val){
val ? props.items.forEach(item => selectedRows.add(item.id)) : selectedRows.clear();
}
});
</script>
<template>
<table>
<thead>
<tr><td><input type="checkbox" v-model="allSelected"></td></tr>
</thead>
<tbody v-for="item in items">
<tr><td><input type="checkbox" :checked="selectedRows.has(item.id)"
@input="selectedRows[selectedRows.has(item.id) ? 'delete' : 'add'](item.id)"/></td><td>{{ item.title }}</td></tr>
<tr><td colspan="2" style="padding-left:32px"><table-select v-if=item.children :items="item.children"/></td></tr>
</tbody>
</table>
</template>