I've been trying to create a simple component with a styled checkbox and corresponding label. The values (strings) of all selected checkboxes should be stored in an array. This works for plain html checkboxes:
<template> <div> <div class="mt-6"> <div> <input type="checkbox" value="EVO" v-model="status" /> <label for="EVO">EVO</label> </div> <div> <input type="checkbox" value="Solist" v-model="status" /> <label for="Solist">Solist</label> </div> <div> <input type="checkbox" value="SPL" v-model="status" /> <label for="SPL">SPL</label> </div> </div> <div class="mt-3">{{status}}</div> </div> </template> <script setup> import { ref } from 'vue' let status = ref([]); </script>
It results in the following desired situation:
Now, if I replace these checkboxes with a custom checkbox component, I can't get it to work. If I check a box, the value it emits seems to replace the status
array instead of being added to or removed from it, resulting in the following:
So for some reason all the checkboxes are checked by default and when I click on one of them they all get unchecked and the status
value goes to false
, clicking any checkbox again will select all checkboxes and make status
true
.
Now, I know that returning whether the box is checked in the emission returns a true or false value, but I don't understand how Vue does this with native checkboxes and how to achieve this behavior with my component. p>
This is the code for my checkbox component:
<template> <div class="mt-1 relative"> <input type="checkbox" :id="id ?? null" :name="name" :value="value" :checked="modelValue ?? false" class="bg-gray-200 text-gold-500 rounded border-0 w-5 h-5 mr-2 focus:ring-2 focus:ring-gold-500" @input="updateValue" /> {{ label }} </div> </template> <script setup> const props = defineProps({ id: String, label: String, name: String, value: String, errors: Object, modelValue: Boolean, }) const emit = defineEmits(['update:modelValue']) const updateValue = function(event) { emit('update:modelValue', event.target.checked) } </script>
And the parent component just uses a different template:
<template> <div> <div class="mt-6"> <Checkbox v-model="status" value="EVO" label="EVO" name="status" /> <Checkbox v-model="status" value="Solist" label="Solist" name="status" /> <Checkbox v-model="status" value="SPL" label="SPL" name="status" /> </div> <div class="mt-3">{{status}}</div> </div> </template>
I tried to look at this answer by StevenSiebert but it uses an object and I want to replicate the original Vue behavior using native checkboxes.
I also referred to the official Vue documentation at v-model
but don't understand why this works differently for native checkboxes than for components.
P粉8934570262023-12-22 15:09:31
The v-model for each checkbox is the same and may resemble the following code snippet:
const { ref } = Vue const app = Vue.createApp({ setup() { const status = ref([{label: 'EVO', status: false}, {label: 'Solist', status: false}, {label: 'SPL', status: false}]) return { status } }, }) app.component('Checkbox', { template: ` <div class="mt-1 relative"> <input type="checkbox" :id="id ?? null" :name="name" :value="value" :checked="modelValue ?? false" class="bg-gray-200 text-gold-500 rounded border-0 w-5 h-5 mr-2 focus:ring-2 focus:ring-gold-500" @input="updateValue" /> {{ label }} </div> `, props:{ id: String, label: String, name: String, value: String, errors: Object, modelValue: Boolean, }, setup(props, {emit}) { const updateValue = function(event) { emit('update:modelValue', event.target.checked) } return { updateValue } } }) app.mount('#demo')
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.18/tailwind.min.css" integrity="sha512-JfKMGsgDXi8aKUrNctVLIZO1k1iMC80jsnMBLHIJk8104g/8WTaoYFNXWxFGV859NY6CMshjktRFklrcWJmt3g==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script> <div id="demo"> <div> <div class="mt-6" v-for="box in status"> <Checkbox v-model="box.status" :value="box.label" :label="box.label" name="status"></Checkbox> </div> <div class="mt-3">{{status}}</div> </div> </div>