The code snippet below demonstrates the power of v-model
. By checking and unchecking checkboxes, the checkedNames
array will automatically add or remove names. No need to manually manipulate the array with push, slice, or filter operations.
const { ref } = Vue;
const App = {
setup () {
const checkedNames = ref([])
return { checkedNames }
}
}
Vue.createApp(App).mount("#app");
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="98eeedfdd8abb6a8b6a9a9">[email protected]</a>/dist/vue.global.js"></script>
<div id="app">
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
I'm wondering how can I achieve a similar feature using custom components?
You'll notice in the following code snippets that I attempted to replicate this functionality but felt it lacked the magic of v-model
. Instead, I had to handle checkbox logic within my function addOrRemoveItem()
.
Just a heads-up: It might seem like I'm over-explaining, apologies for that.
I referenced this tutorial for some guidance, which wasn't very helpful. Then, I consulted the Vue official documentation here. Below is the functional code, but it seems a bit excessive.
const { ref, createApp } = Vue;
const app = createApp({
setup() {
const itemsSelected = ref([]);
const items = ref([
{ id: '1', name: 'Jack' },
{ id: '2', name: 'John' },
{ id: '3', name: 'Mike' },
]);
const addOrRemoveItem = (itemId) => {
const exists = itemsSelected.value.includes(itemId);
if (exists) {
itemsSelected.value = itemsSelected.value.filter((id) => id !== itemId);
} else {
itemsSelected.value.push(itemId);
}
};
return {
items,
itemsSelected,
addOrRemoveItem,
};
},
});
app.component('custom-checkbox', {
props: {
item: { type: Object, default: () => ({}) },
modelValue: { type: Array, default: () => [] },
},
template: `
<div>
<input
type="checkbox" :value="item.id"
@change="$emit('update:model-value', $event.target.checked)"
> {{ item.name }}
</div>
`
})
app.mount("#app");
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8cfaf9e9ccbfa2bca2bdbd">[email protected]</a>/dist/vue.global.js"></script>
<div id="app">
<div><code>itemsSelected: {{ itemsSelected }}</code></div>
<hr />
<custom-checkbox
v-for="item in items"
:key="item.id"
:item="item"
:model-value="itemsSelected"
@update:model-value="addOrRemoveItem(item.id)"
></custom-checkbox>
</div>
As mentioned earlier, the code seemed cumbersome when following the instructions from the Vue documentation $emit('update:model-value', ...)
, where it can be simplified to something like $emit('val-updated')
. Here's a streamlined version after removing unnecessary props and shortening the $emit
event.
const { ref, createApp } = Vue;
const app = createApp({
setup() {
const itemsSelected = ref([]);
const items = ref([
{ id: '1', name: 'Jack' },
{ id: '2', name: 'John' },
{ id: '3', name: 'Mike' },
]);
const addOrRemoveItem = (itemId) => {
const exists = itemsSelected.value.includes(itemId);
if (exists) {
itemsSelected.value = itemsSelected.value.filter((id) => id !== itemId);
} else {
itemsSelected.value.push(itemId);
}
};
return {
items,
itemsSelected,
addOrRemoveItem,
};
},
});
app.component('custom-checkbox', {
props: {
item: { type: Object, default: () => ({}) },
},
template: `
<div>
<input
type="checkbox" :value="item.id"
@change="$emit('val-updated')"
> {{ item.name }}
</div>
`
})
app.mount("#app");
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="91e7e4f4d1a2bfa1bfa0a0">[email protected]</a>/dist/vue.global.js"></script>
<div id="app">
<div><code>itemsSelected: {{ itemsSelected }}</code></div>
<hr />
<custom-checkbox
v-for="item in items"
:key="item.id"
:item="item"
@val-updated="addOrRemoveItem(item.id)"
></custom-checkbox>
</div>