Reputation: 13
I have written a custom Vue3 component for form input, and parent components should be able to use v-model to bind the value of the input field. I have done this successfully in basic form (which is shown in my first snippet below). However, it only seems to work when the input element is the parent element in the component template. If I surround the input element in a div (within the custom component), it stops working.
This is the working component in its basic form.
<template>
<input v-model="value" class="form-control form-in" type="text" :name="name" :placeholder="placeholder" @input="updateValue()"/>
</template>
<script>
export default {
name: 'FormInput',
props: {
name: String,
placeholder: String,
},
data () {
return {
value: ""
}
},
methods: {
updateValue() {
this.$emit('update:modelValue', this.value)
}
}
}
</script>
<style lang="scss" scoped>
</style>
This is utilised in a parent Vue file.
<div class="row mb-3">
<div class="col-md-12">
<label class="ml-2">Name</label>
<FormInput
name="name"
v-model="form.name"
ref="name"
/>
</div>
</div>
</div>
This is all working as expected. When I type in this input, the value of 'form.name' is being updated correctly. However, I wanted to add some more features to my input component, e.g. a label afterwards that could be used for a validation message. As a Vue component requires a single parent component, I surround both the input and label elements in a div.
<template>
<div>
<input v-model="value" :class="{ 'validation-failed' : validation_failed == true}" class="form-control form-in" type="text" :name="name" :placeholder="placeholder" @input="updateValue()"/>
<label class="font-weight-bold ml-2 mt-1 text-danger" v-if="validation_failed">{{ validation_msg }}</label>
</div>
</template>
I notice now that the v-model binding on the parent component is no longer working. To troubleshoot I rolled this all back, and started by just adding the div tag:
<template>
<div>
<input v-model="value" class="form-control form-in" type="text" :name="name" :placeholder="placeholder" @input="updateValue()"/>
</div>
</template>
This also isn't working, so it's to do with the input being inside a div.
When I debug the 'FormInput' component, I can see its 'value' is being updated with the text input.
I'm assuming I'm missing some fundamental rule of Vue as to why this is not working, but I haven't been able to figure this out from Googling.
Upvotes: 1
Views: 2753
Reputation: 22769
You could make some tweaks here:
v-model
on <input>
there's no need to use its @input
otherwise do just 1-way binding without v-model
v-model
implementation with computed
validation_failed
since the presence of validation_msg
is enough<script setup>
as the latest recommend way to make components in Vue 3.<script>
as the first section, again the latest recommended way<template>
<input v-model="value" :class="{ 'validation-failed' : validationMsg}" class="form-control form-in" type="text" :name="name" :placeholder="placeholder"/>
<label class="font-weight-bold ml-2 mt-1 text-danger" v-if="validationMsg">{{ validationMsg }}</label>
</template>
<script>
export default {
name: 'FormInput',
props: {
name: String,
placeholder: String,
modelValue: String
},
computed: {
validationMsg(){
return this.value.match(/^\d*$/) ? '' : 'Not a number!!!'
},
value: {
get(){
return this.modelValue;
},
set(value){
this.$emit('update:modelValue', value)
}
}
}
}
</script>
Upvotes: 0