The Component I am making is a basic toggle switch made in vue using tailwind. during the initial render the switch is in an incorrect position, first click toggles the theme but the switch still stays the same, third click and forward the switch works properly
Switch.vue
<template>
<label class="switch relative inline-block w-[39px] h-[19px]">
<input type="checkbox" class="w-0 h-0 opacity-0" v-model="modelValue">
<span
class="absolute rounded-[34px] before:rounded-[50%] cursor-pointer top-0 left-0 right-0 bottom-0 bg-gray-300 transition shadow-inner before:absolute before:h-[17px] before:w-[17px] before:left-[1px] before:top-[1px] before:bg-white before:transition "
:class="[{
'before:translate-x-5 bg-blue-500': modelValue
}]" @click="$emit('update:modelValue', modelValue)"></span>
</label>
</template>
Parent.vue
// darkTheme comes from a pinia store.
<Switch v-model="darkTheme.enableDarkTheme" />
I was expecting the switch to work with v-model and toggle the theme, it works after a few clicks but first 3 clicks it does not toggle.
import { defineStore } from "pinia";
export let useThemeStore = defineStore('darkTheme', {
state: () => {
return {
enableDarkTheme: window.matchMedia('(prefers-color-scheme: dark)').matches,
}
},
actions: {
toggle() {
!this.enableDarkTheme;
}
}
})
Edit: Pinia seems to be sending the prop value as undefined on the first mutation, which seems to be causing the issue. please verify the store code
Referring to custom components v-model in the docs https://vuejs.org/guide/components/v-model.html for v-model in custom components props and emits need to be explicitly defined in the script setup, also see @Estus's comment about nested v-model
This is a simple vue component for a toggle slider Switch.vue
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<label class="switch relative inline-block w-[39px] h-[19px]">
<input type="checkbox" class="w-0 h-0 opacity-0" :checked="modelValue">
<span
class="absolute rounded-[34px] before:rounded-[50%] cursor-pointer top-0 left-0 right-0 bottom-0 bg-gray-300 transition shadow-inner before:absolute before:h-[17px] before:w-[17px] before:left-[1px] before:top-[1px] before:bg-white before:transition "
:class="[{
'before:translate-x-5 bg-blue-500': modelValue
}]" @click="$emit('update:modelValue', !modelValue)"></span>
</label>
</template>
Parent Component
//Inside the parent component
<Switch v-model="darkTheme.enableDarkTheme" />