Vue v-model Directive
Example
Using the v-model
directive to create a two-way binding between an <input>
element and a data property.
<template>
<h1>v-model Example</h1>
<p>Write something, and see the 'inputValue' data property update automatically.</p>
<input type="text" v-model="inputValue">
<p>inputValue property: "{{ inputValue }}"</p>
</template>
Run Example »
See more examples below.
Definition and Usage
The v-model
directive is used to create a two-way binding between a form input element, or between a Vue instance property and a component.
Form Input Elements with v-model
Form input elements that can be used with v-model
are <input>
, <select>
and <textarea>
.
Two-way binding with v-model
on form input elements works like this:
- When Vue detects a change in the input value, it will update the corresponding data property accordingly. (HTML -> JavaScript)
- When Vue detects a change in a Vue instance property, it will update the corresponding input value accordingly. (JavaScript -> HTML)
(See Example above, and Example 1 below.)
Components with v-model
When v-model
is used on a component, the component interface must be set up properly with props
and emits
to achieve the two-way binding.
Two-way binding with v-model
on components works like this:
- When Vue detects a change in the parent instance property, the new value is sent as a prop to the component.
- When Vue detects a change in the child component, the new value is sent up to the parent as an emit event.
When v-model
is used on a component, the default prop name is 'modelValue', and the default emit event name is 'update:modelValue'. (See Example 2 and Example 3.)
When v-model
is used on a component, instead of using a Vue instance data property we can use a computed property with the get()
and set()
methods. (See Example 4)
Different names for props and emits than the default 'modelValue' and 'update:modelValue' can be set using v-model:
. (See Example 5)
To have more than one value connected as two-way bindings to a component, we must define each such value with its own v-model
. (See Example 6)
Modifiers
Modifiers | Details |
---|---|
.lazy |
The change event is used by Vue instead of the input event for when to synchronize. This means that the user must first modify the input, and then switch focus away from the input element before the instance property value gets updated. (See Example 7) |
.number |
Typecasts the input to number. This is done automatically when using <input type="number"> . |
.trim |
Removes white spaces at the start and end of the input. (See Example 8) |
custom | To create a custom modifier for v-model , we first need to define a prop 'modelModifiers' to store the new modifier. The modifier functionality is written in a method. If the modifier is set, the appropriate code runs in the method before emitting the value back up to the parent component. (See Example 9) |
More Examples
Example 1
Using a slider (<input type="range">
) to change the 'inputValue' property value. The <input type="text">
element automatically updates because it is bound to the 'inputValue' property with v-model
.
<template>
<h1>v-model Example</h1>
<p>Drag the slider to change the 'inputValue' data property, and see the input text field update automatically because of the two-way binding from v-model.</p>
<input type="range" min="-50" max="50" v-on:input="sliderChange" value="4">
<p>inputValue property: "{{ inputValue }}"</p>
<input type="text" v-model="inputValue">
</template>
<script>
export default {
data() {
return {
inputValue: null
};
},
methods: {
sliderChange(evt) {
this.inputValue = evt.target.value
}
}
}
</script>
Run Example »
Example 2
Using v-model
on the component with props
and emits
so that changes in the <input>
element updates the parent's 'text' property.
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>App.vue 'text' property: "{{ text }}"</p>
<comp-one v-model="text"/>
</template>
<script>
export default {
data() {
return {
text: 'Say Cheese'
}
}
}
</script>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Write something in the text input field below to see that changes here are emitted from the component, and the parent 'text' property gets updated by the use of v-model.</p>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue']
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 500px;
}
</style>
Run Example »
Example 3
Using v-model
on the component to demonstrate the two-way binding more clearly. The component can update the parent 'text' property, and the component gets updated when the parent 'text' property is changed.
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>App.vue 'text' property: "<pre>{{ text }}</pre>"</p>
<button v-on:click="this.text = 'Hello!'">text='Hello!'</button>
<comp-one v-model="text"/>
</template>
<script>
export default {
data() {
return {
text: 'Say Cheese'
}
}
}
</script>
<style>
pre {
display: inline;
background-color: yellow;
}
</style>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Two-way binding on component with v-model:</p>
<ol>
<li>The component can update the 'text' property (using text field).</li>
<li>The component gets updated when the 'text' property is changed (using button).</li>
</ol>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue']
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 600px;
}
</style>
Run Example »
Example 4
Using v-model
with a computed value with get()
and set()
functions inside the component.
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Two-way binding on component with v-model:</p>
<ol>
<li>The component can update the 'text' property (using text field).</li>
<li>The component gets updated when the 'text' property is changed (using button).</li>
</ol>
<input v-model="inpVal"/>
</div>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
computed: {
inpVal: {
get() {
return this.modelValue;
},
set(inpVal) {
this.$emit('update:modelValue',inpVal)
}
}
}
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 600px;
}
</style>
Run Example »
Example 5
Using v-model:message
on the component to rename the default prop name 'modelValue' to 'message'.
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>App.vue 'text' property: "<pre>{{ text }}</pre>"</p>
<button v-on:click="this.text = 'Hello!'">text='Hello!'</button>
<comp-one v-model:message="text"/>
</template>
<script>
export default {
data() {
return {
text: 'Say Cheese'
}
}
}
</script>
<style>
pre {
display: inline;
background-color: yellow;
}
</style>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Two-way binding on component with v-model:</p>
<ol>
<li>The component can update the 'text' property (using text field).</li>
<li>The component gets updated when the 'text' property is changed (using button).</li>
</ol>
<input
:value="message"
@input="$emit('update:message', $event.target.value)"
/>
</div>
</template>
<script>
export default {
props: ['message'],
emits: ['update:message']
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 600px;
}
</style>
Run Example »
Example 6
Using v-model
two times on the component to create a two-way binding with two values.
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>Name: "<pre>{{ name }}</pre>"</p>
<p>Height: <pre>{{ height }}</pre> cm</p>
<comp-one
v-model:name="name"
v-model:height="height"
/>
</template>
<script>
export default {
data() {
return {
name: 'Olaf',
height: 120
}
}
}
</script>
<style>
pre {
display: inline;
background-color: yellow;
}
</style>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Two inputs are bound to the component with v-model through props and emits.</p>
<p>
<label>
Name:
<input
type="text"
:value="name"
@input="$emit('update:name', $event.target.value)"
/>
</label>
</p>
<p>
<label>
Height:
<input
type="range"
:value="height"
@input="$emit('update:height', $event.target.value)"
min="50"
max="200"
/>
{{ this.$props.height }} cm
</label>
</p>
</div>
</template>
<script>
export default {
props: ['name','height'],
emits: ['update:name','update:height']
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 300px;
}
</style>
Run Example »
Example 7
Using the .lazy
so that the user must first modify the input element, then change focus away from the input element before the property gets updated with v-model
.
<template>
<h1>v-model Example</h1>
<p>Using the '.lazy' modifier, you must first write something, then click somewhere else, or use the tab key to switch focus away from the input element, before the property get updated.</p>
<input type="text" v-model.lazy="inputValue">
<p>inputValue property: "{{ inputValue }}"</p>
</template>
<script>
export default {
data() {
return {
inputValue: null
};
}
}
</script>
Run Example »
Example 8
Using the .lazy
so that the user must first modify the input element, then change focus away from the input element before the property gets updated with v-model
.
<template>
<h1>v-model Example</h1>
<p>Using the '.trim' modifier will remove any white spaces at the start and end of the input.</p>
<p>Add white spaces at the start and end in the input fields below to see the difference with or with out '.trim'.</p>
<p>No '.trim': <input type="text" v-model="inputVal1"> "<pre>{{ inputVal1 }}</pre>"</p>
<p>With '.trim': <input type="text" v-model.trim="inputVal2"> "<pre>{{ inputVal2 }}</pre>"</p>
</template>
<script>
export default {
data() {
return {
inputVal1: 'Hello',
inputVal2: 'Hi'
};
}
}
</script>
<style>
pre {
display: inline;
background-color: lightgreen;
}
</style>
Run Example »
Example 9
Using the custom .allCapital
modifier to convert all characters in the input to upper case if the .allCapital
modifier is set.
App.vue
:
<template>
<h2>Example v-model Directive</h2>
<p>App.vue 'text' property: "{{ text }}"</p>
<comp-one v-model.allCapital="text"/>
</template>
<script>
export default {
data() {
return {
text: ''
}
}
}
</script>
CompOne.vue
:
<template>
<div>
<h3>Component</h3>
<p>Write something in the text input field below. Click somewhere else or use the tab key to shift focus away from the input element to see the effect of the custom 'allCapital' modifier.</p>
<input
:value="modelValue"
@change="this.emitVal"
/>
</div>
</template>
<script>
export default {
props: {
modelValue: String,
modelModifiers: {
// modelModifiers is an empty object initially.
// Modifiers set on the component will be stored here.
default: () => ({})
}
},
emits: ['update:modelValue'],
methods: {
emitVal(e) {
let value = e.target.value
if (this.modelModifiers.allCapital) {
value = value.toUpperCase()
}
this.$emit('update:modelValue', value)
}
}
}
</script>
<style scoped>
div {
border: solid black 1px;
padding: 10px;
margin: 20px 0;
max-width: 500px;
}
</style>
Run Example »
Related Pages
Vue Tutorial: Vue Components
Vue Tutorial: Vue Props
Vue Tutorial: Vue $emit() Method
Vue Tutorial: Vue Computed Properties
Vue Reference: Vue $emit() Method
Vue Reference: Vue $props Object
JavaScript Tutorial: JavaScript Object Accessors