This article demonstrates how to implement v-model functionality in custom components, using input as an example. We'll create a custom component named z-input that can be used with v-model in a parent component like this:
<template>
<z-input v-model="msg" />
</template>
<!-- JS code omitted -->
Both Vue 2 and Vue 3.2 implementations are covered.
Vue 2
You can create a Vue 2 project with either vue-cli or vite. For vite, you can follow the guide "Setting up a Vue2 project with Vite (Vue2 + vue-router + vuex)".
Example
Parent component
<template>
<div>
<z-input v-model="msg" />
<p>{{ msg }}</p>
</div>
</template>
<script>
import ZInput from './ZInput'
export default {
components: {
ZInput
},
data() {
return {
msg: 'Hello'
}
}
}
</script>
Custom component (z-input)
<template>
<input type="text"
:value="msg1"
@input="$emit('change1', $event.target.value)">
</template>
<script>
export default {
model: {
prop: 'msg1', // corresponds to the prop
event: 'change1' // event to emit
},
props: {
msg1: {
type: String,
default: ''
}
}
}
</script>
Explanation
By default, v-model on a component uses a value prop and emits an input event. However, in the example, we didn't use value. Instead, we configured the componant with the model option.
model.proptells Vue which prop should receive thev-modelvalue. Here it ismsg1.model.eventspecifies the name of the event to emit when the value changes. Here it ischange1.
In the template, we bind the prop to the input's value attribute and emit the change1 event with the new input value ($event.target.value) on each input event.
This way, the custom component can use a different prop name (msg1) and event name (change1) while still supporting v-model.
Vue 3.2
This example uses the <script setup> syntax. If you're unfamiliar with using v-model in <script setup>, you can check "Vue3: Over 10 Component Communication Methods".
Example
Parent component
<template>
<Child v-model="message" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const message = ref('hello')
</script>
Custom component (Child)
<template>
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)">
</template>
<script setup>
const props = defineProps(['modelValue'])
</script>
In Vue 3, the default prop name for v-model is modelValue, and the default event name is update:modelValue. The custom component receives the value via modelValue and emits update:modelValue with the new value.
This is essential the same mechanism as Vue 2, but with different naming conventions.