Reputation: 212
I just found out that in Vue3, v-model
is not working responsively / reactively with child Component
.
This code will update the username
data
<template>
<div>
<input type="text" v-model="username" placeholder="Insert your username" />
<p>{{ username }}</p>
</div>
</template>
<script>
// Home.vue
export default {
name: 'Home',
data() {
return {
username: 'admin'
}
}
}
</script>
If I type something in the input
, the username
data will change too.
But, when I use Component
like this example:
<template>
<input type="text" :class="'input-text ' + additionalClass" :placeholder="placeholder" />
</template>
<script>
// InputText.vue
import { defineComponent } from "vue"
export default defineComponent({
name: 'InputText',
props: {
placeholder: {
type: String,
default: ''
},
additionalClass: {
type: String,
default: ''
}
}
})
</script>
Then I updated my code to use the Component
.
Note: The Component
is registered successfully.
<template>
<div>
<input-text v-model="username" :placeholder="`Insert your username`" />
<p>{{ username }}</p>
</div>
</template>
<script>
// Home.vue
export default {
name: 'Home',
data() {
return {
username: 'admin'
}
}
}
</script>
When I type something, the username
data not updated, different with the previous one.
Is there any solution or at least reference of what I'm trying to achieve?
Upvotes: 8
Views: 17245
Reputation: 670
Starting vue 3.4 recommended approach is to use defineModel()
macro.
https://vuejs.org/guide/components/v-model
Based on your answer the code will looks like (with TypeScript types usage):
<script setup lang="ts">
const model = defineModel();
const props = defineProps<{
placeholder: string
additionalClass: string
}>();
</script>
<template>
<input
type="text"
v-model="model"
:class="'input-text ' + props.additionalClass"
:placeholder="props.placeholder"
/>
</template>
And in your parent component
<script setup lang="ts">
const textInput = ref('');
</script>
<template>
<SomeInput
v-model="textInput"
placeholder="placeholder"
additionalClass="some-class"
/>
</template>
Upvotes: 0
Reputation: 311
with Vue3 script setup
syntax
<template>
<input type="text" v-model="val" @input="handleInput">
</template>
<script setup>
import {ref, defineProps, defineEmits, watch} from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: ''
},
})
const emit = defineEmits(['update:modelValue'])
const val = ref('')
watch(() => props.modelValue, nVal => val.value = nVal, {
immediate: true
})
const handleInput = () => emit('update:modelValue', val.value)
</script>
Upvotes: 16
Reputation: 9200
You can't expect v-model
to implicitly update the underlying element for you. In other words, you'll still need to handle that within the component itself and expose modelValue
as a prop for this to really work. Something like that:
<template>
<input
type="text"
@input="onChanged"
:value="modelValue"
:class="'input-text ' + additionalClass"
:placeholder="placeholder" />
</template>
<script>
// InputText.vue
import { defineComponent } from "vue"
export default defineComponent({
name: 'InputText',
emits: ['update:modelValue'],
props: {
modelValue: String,
placeholder: {
type: String,
default: ''
},
additionalClass: {
type: String,
default: ''
}
},
setup(props, { emit }) {
function onChanged(e) {
emit('update:modelValue', e.currentTarget.value);
}
return {
onChanged
}
}
})
</script>
Upvotes: 12
Reputation: 1966
The VueUse helper useVModel
can simplify handling v-model
in a sub-component, see https://vueuse.org/core/usevmodel/#usage
<script lang="ts" setup>
import { useVModel } from '@vueuse/core'
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits(['update:modelValue'])
const model = useVModel(props, 'modelValue', emit)
</script>
<template>
<input type="text" v-model="model" />
</template>
Upvotes: 7
Reputation: 562
with OPTIONS API
syntax
<template>
<input type="text" v-model="val" @input="updateText" :value="modelValue">
</template>
<script scoped>
export default {
props: {
modelValue: String,
},
methods: {
updateText(event){
this.$emit('update:modelValue', event.currentTarget.value)
}
}
}
</script>
Upvotes: 2
Reputation: 1589
In vue2, this thing worked:
Your InputText component
<template>
<input :value="value"
@input="({target}) => $emit('input', target.value)"/>
</template>
<script>
export default {
props: {
value: {}
}
}
Your parent component
<template>
<inputText v-model="inputValue"/>
</template>
<script>
export default {
components: {inputText},
data: () => ({
inputValue: null
})
}
</script>
Upvotes: -1