Vue 3 custom checkbox component bound to array of selected values
P粉561749334
P粉561749334 2023-12-21 18:43:52
0
1
671

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.

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粉561749334
P粉561749334

reply all(1)
P粉893457026

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>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template