Vue.js has a v-model
attribute which is pretty handy for quickly and easily binding HTML input elements to JavaScript variables. Eventually you might wonder - what is v-model
doing under the hood? What is it syntactic sugar for? Or, you might be trying to build a stateless component and v-model doesn’t exactly work nicely with that pattern.
A Simple Example
In this example, we replace v-model
with the :value
prop and @input
event for a textbox. You can easily customize what happens when the input changes by modifying the event handler:
<!-- v-model syntax -->
<input v-model="x" />
<!-- Equivalent using props / events -->
<input :value="x" @input="x = $event.target.value" />
Whether you’re using single-file components or the newer composition API, this is still applicable. To get the most out of this page, should already be familiar with the v-model section of the Vue.JS documentation: https://vuejs.org/guide/essentials/forms#form-input-bindings
In this document, I’m using $event
for brevity, but you can use an arrow function as an event handler. For example, @input="x = $event.target.value"
can be written as @input="event => x = event.target.value"
.
HTML Inputs
Bindings for traditional HTML input elements such as checkboxes, radio buttons, text inputs, etc.
Text Input / Text Area
This works for both text inputs and textarea elements.
<!-- v-model syntax -->
<input v-model="x" />
<!-- Equivalent using Props -->
<input :value="x" @input="x = $event.target.value" />
Checkbox
A checkbox is very similar to a text input, except we’re using the checked
attribute instead of value to get the boolean state of the checkbox.
<!-- v-model syntax -->
<input type="checkbox" v-model="x" />
<!-- Equivalent using Props -->
<input type="checkbox" :checked="x" @change="x = $event.target.checked" />
Radio Buttons
Since radio buttons are typically grouped and when selected they emit a specific value, we need to determine checked
depending on whether or not our variable equals the value for the radio button. After, when the radio button is selected, we need to update our variable to the value of the radio button.
Technically, you don’t need value
, and can do the same thing as the above checkbox example, but if trying to mimic a traditional HTML input this is likely what you’ll need.
<!-- v-model syntax -->
<input type="radio" value="A" v-model="x" />
<input type="radio" value="B" v-model="x" />
<!-- Equivalent using Props -->
<input
type="radio"
value="A"
:checked="x === 'A'"
@change="x = $event.target.value"
/>
<input
type="radio"
value="B"
:checked="x === 'B'"
@change="x = $event.target.value"
/>
Select Dropdown
<!-- v-model syntax -->
<select v-model="x">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
<!-- Equivalent using Props -->
<select :value="x" @change="x = $event.target.value">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
Custom Components
In Vue.js 2.0, the prop used by v-model for custom compoents was called value
and the event it emitted on changes was the @input
event.
Now, in Vue 3, it’s called modelValue
, and the event is @update:modelValue
. Note <input>
tags, etc. still use the value
prop as that matches how you interact with the element using its vanilla JS property.
<!-- v-model syntax -->
<MyComponent v-model="x" />
<!-- in VueJS 2.0, this is what this is equivalent to -->
<!-- *WILL NOT WORK IN VUE 3.0* -->
<MyComponent :value="x" @input="x = $event" />
<!-- in VueJS 3.0, this is what this is equivalent to -->
<MyComponent :modelValue="x" @update:modelValue="x = $event" />
Binding Multiple Values
Vue 3.0 also supports multiple bindings. Any prop that confirms to the propName
/ @update:propName
pattern can be used with v-model
. This is useful for components that have multiple values to bind to. For example, if my prop name is a
, I can use v-model:a="someVar"
to bind to it.
<!-- v-model syntax -->
<SomeCustomComponent v-model:a="someVar" v-model:b="someOtherVar" />
<!-- Equivalent using Props -->
<SomeCustomComponent
:a="someVar"
:b="someOtherVar"
@update:a="someVar = $event"
@update:b="someOtherVar = $event"
/>
Modifiers
Vue also supports modifiers for v-model
to handle different input types. Here’s how you can handle them using props:
Number Binding - v-model.number
This coerces the input value to a number. .number
is automatically applied when an input’s type
is number
. This conversion is only done if the number is able to be parsed. So, using this on a textarea won’t force the value to be a number.
<!-- v-model syntax -->
<input type="number" v-model="x" />
<!-- Equivalent using Props -->
<input type="number" :value="x" @input="event => {
const parsedValue = parseFloat(event.target.value)
x = isNaN(value) ? event.target.value : parsedValue
}" />
Trimmed Strings - v-model.trim
This trims the input value before assigning it
<!-- v-model syntax -->
<input v-model.trim="x" />
<!-- Equivalent using Props -->
<input :value="x" @input="x = $event.target.value.trim()" />
Lazy Updates - v-model.lazy
This updates the value after the @change
event, rather than on every @input
event.
<!-- v-model syntax -->
<input v-model.lazy="x" />
<!-- Equivalent using Props -->
<input :value="x" @change="x = $event.target.value" />