In my parent component I have something similar to this,
<template>
<ProductCounter v-model="formData.productCount" label="product count" />
</template>
<script setup>
const initialFormData = {
productCount: null,
firstname: '',
surname: '',
phone: '',
email: '',
postcode: '',
submittedOnce: false,
errors: []
}
let formData = reactive({ ...initialFormData });
const clearUI = () => {
formData = reactive({ ...initialFormData });
triggerInlineForm.value = false;
}
</script>
My child component looks like this,
<template>
<div class="form__row" @reset-counts="resetCount">
<div class="counter__row">
<label>{{ label }}</label>
<div class="form__counter">
<button class="form__button--decrease form__button--circle form__button--animate-scale" :disabled="value == 0 || props.disabled" @click.prevent="decreaseCount()">
<i>
<FontAwesomeIcon :icon="['fal', 'minus']" />
</i>
</button>
<input type="text" v-model="value" :disabled="props.disabled" @input="updateQty" placeholder="0"/>
<button class="form__button--increase form__button--circle form__button--animate-scale" :disabled="props.disabled" @click.prevent="increaseCount()">
<i>
<FontAwesomeIcon :icon="['fal', 'plus']" />
</i>
</button>
</div>
</div>
</div>
</template>
<script setup>
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
const emits = defineEmits(['update:modelValue', 'resetCounts']);
const props = defineProps({
label: {
type: String,
required: true
},
modelValue: {
type: String,
required: true,
default: 0
},
disabled: {
type: Boolean,
required: false
}
});
const value = ref(props.modelValue);
const updateQty = () => {
emits('update:modelValue', value.value)
}
const increaseCount = () => {
value.value++
emits('update:modelValue', value.value)
}
const decreaseCount = () => {
value.value--;
emits('update:modelValue', value.value)
}
</script>
I would expect that when clearUI is fired from the parent, and formData gets reset the v-model of ProductCounter should reflect that go back to 0 but it does not, where am I going wrong?
2
Answers
The link to live solution
Please prepare minimum reproducible example the next time on https://play.vuejs.org/. And to your question:
You SHOULD NOT overwrite reactive variables in Vue please…
Just mutate them
Object.assign(formData, initialFormData)
:Also don’t dereference component properties:
const value = ref(props.modelValue)
. The properties lose their reactivity because you just a copy a primitive value.The best way to create a
v-model
pattern is to usecomputed
which you can manipulate directly in the template.Also your count property should be a number, not a string (you get Vue warnings):
Also there’s no need to update the prop on the
input
event, since you’re already usingv-model
on the<input>
. Also you should convert your input’s model to a number:So you have:
App.vue
ProductCounter.vue:
When you override
formData
inclearUI()
, you change the content of the variable:However, this does not change the object that was bound to the template during component setup. You can fix this using
ref
and assigning to its value:or you can override the properties individually:
A second problem is that you are setting the
value
inProductCounter
to the initial value ofprops.modelValue
, but that is the literal value, not a reactive property. So whenprops.modelValue
changes,value
does not. To fix it, you can add a watcher:Now
value
will be adjusted whenprops.modelValue
changes.Here it is in a playground