I'm trying to create custom Input component with composition API in Vue 3 but when I'm trying to update value with v-model I am getting empty string instead of event value and when I replace custom Input
component with default HTML input
the value is being updated as expected
Input component:
<template>
<input
:type="type"
:id="name"
:name="name"
:placeholder="placeholder"
class="input"
v-model="modelValue"
/>
</template>
<script lang="ts">
import { computed } from 'vue';
export default {
name: 'Input',
props: {
modelValue: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
type: {
type: String,
default: 'text',
},
placeholder: {
type: String,
required: true,
},
},
setup(props: { value: string }, { emit }) {
const modelValue = computed({
get() {
return props.modelValue;
},
set(value) {
emit('input', value);
},
});
return { modelValue };
},
};
</script>
<form @submit.prevent="handleSubmit">
<Input :name="name" placeholder="Name*" v-model="name" />
<Button>Send</Button>
</form>
Setup method:
setup() {
const name = ref('');
function handleSubmit(data: Event) {
console.log(data, name.value);
}
watch(name, (old, newValue) => {
console.log(name, old, newValue);
});
return { name, handleSubmit };
},
CodePudding user response:
There are a couple of errors & warnings in your code:
- you should declare emitted events in the
emits
option (more on this here) - you did not have a
value
prop passed down from the parent component toInput
(thus I removed it) - if you want to do the "easy sync" with
v-model
, then it's best to emit a custom event calledupdate:modelValue
(wheremodelValue
is the value you want to bind as prop, e.g.update:name
; more on this here) - you should NOT name a variable in the
setup
function the same as aprop
(this is just sensibility - you'll mess up what is what, eventually)
const {
computed,
ref,
} = Vue
const Input = {
name: 'Input',
props: {
name: {
type: String,
required: true,
},
type: {
type: String,
default: 'text',
},
placeholder: {
type: String,
required: true,
},
},
emits: ['update:name'],
setup(props, { emit }) {
const modelValue = computed({
get() {
return props.name;
},
set(value) {
emit('update:name', value);
},
});
return {
modelValue
};
},
template: `
<input
:type="type"
:id="name"
:name="name"
:placeholder="placeholder"
v-model="modelValue"
/>
`
}
const App = {
setup() {
const name = ref('');
function handleSubmit(data) {
console.log(data, name.value);
}
return {
name,
handleSubmit
};
},
template: `
Name Ref: {{ name }}<br />
<form @submit.prevent="handleSubmit">
<Input
:name="name"
placeholder="Name*"
v-model="name"
/>
<button type="submit">Send</button>
</form>
`
}
const vm = Vue.createApp(App)
vm.component('Input', Input)
vm.mount('#app')
<script src="https://unpkg.com/vue@next"></script>
<div id="app"></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>