Handling Forms

The Basics

You can use the v-model directive to create two-way data bindings on form input elements. It automatically picks the correct way to update the element based on the input type.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<form id="demo">
<!-- text -->
<p>
<input type="text" v-model="msg">
{{msg}}
</p>
<!-- checkbox -->
<p>
<input type="checkbox" v-model="checked">
{{checked ? "yes" : "no"}}
</p>
<!-- radio buttons -->
<p>
<input type="radio" name="picked" value="one" v-model="picked">
<input type="radio" name="picked" value="two" v-model="picked">
{{picked}}
</p>
<!-- select -->
<p>
<select v-model="selected">
<option>one</option>
<option>two</option>
</select>
{{selected}}
</p>
<!-- multiple select -->
<p>
<select v-model="multiSelect" multiple>
<option>one</option>
<option>two</option>
<option>three</option>
</select>
{{multiSelect}}
</p>
<p><pre>data: {{$data | json 2}}</pre></p>
</form>
1
2
3
4
5
6
7
8
9
10
new Vue({
el: '#demo',
data: {
msg : 'hi!',
checked : true,
picked : 'one',
selected : 'two',
multiSelect: ['one', 'three']
}
})

Result

{{msg}}

{{checked ? "yes" : "no"}}

{{picked}}

{{selected}}

{{multiSelect}}

data:

{{$data | json 2}}

Lazy Updates

By default, v-model syncs the input with the data after each input event. You can add a lazy attribute to change the behavior to sync after change events:

1
2
<!-- synced after "change" instead of "input" -->
<input v-model="msg" lazy>

Casting Value as Number

If you want user input to be automatically persisted as numbers, you can add a number attribute to your v-model managed inputs:

1
<input v-model="age" number>

Bind to Expressions

^0.12.12 only

When using v-model on checkbox and radio inputs, the bound value is either a boolean or a string:

1
2
3
4
5
<!-- toggle is either true or false -->
<input type="checkbox" v-model="toggle">

<!-- pick is "red" when this radio box is selected -->
<input type="radio" v-model="pick" value="red">

This can be a bit limiting - sometimes we may want to bind the underlying value to something else. Here’s how you can do that:

Checkbox

1
<input type="checkbox" v-model="toggle" true-exp="a" false-exp="b">
1
2
3
4
// when checked:
vm.toggle === vm.a
// when unchecked:
vm.toggle === vm.b

Radio

1
<input type="radio" v-model="pick" exp="a">
1
2
// when checked:
vm.pick === vm.a

Dynamic Select Options

When you need to dynamically render a list of options for a <select> element, it’s recommended to use an options attribute together with v-model so that when the options change dynamically, v-model is properly synced:

1
<select v-model="selected" options="myOptions"></select>

In your data, myOptions should be an keypath/expression that points to an Array to use as its options.

The options Array can contain plain strings:

1
options = ['a', 'b', 'c']

Or, it can contain objects in the format of {text:'', value:''}. This object format allows you to have the option text displayed differently from its underlying value:

1
2
3
4
options = [
{ text: 'A', value: 'a' },
{ text: 'B', value: 'b' }
]

Will render:

1
2
3
4
<select>
<option value="a">A</option>
<option value="b">B</option>
</select>

The value can also be Objects:

0.12.11+ only

1
2
3
4
options = [
{ text: 'A', value: { msg: 'hello' }},
{ text: 'B', value: { msg: 'bye' }}
]

Option Groups

Alternatively, the object can be in the format of { label:'', options:[...] }. In this case it will be rendered as an <optgroup>:

1
2
3
4
[
{ label: 'A', options: ['a', 'b']},
{ label: 'B', options: ['c', 'd']}
]

Will render:

1
2
3
4
5
6
7
8
9
10
<select>
<optgroup label="A">
<option value="a">a</option>
<option value="b">b</option>
</optgroup>
<optgroup label="B">
<option value="c">c</option>
<option value="d">d</option>
</optgroup>
</select>

Options Filter

It’s quite likely that your source data does not come in this desired format, and you will have to transform the data in order to generate dynamic options. In order to DRY-up the transformation, the options param supports filters, and it can be helpful to put your transformation logic into a reusable custom filter:

1
2
3
4
5
Vue.filter('extract', function (value, keyToExtract) {
return value.map(function (item) {
return item[keyToExtract]
})
})
1
2
3
4
<select
v-model="selectedUser"
options="users | extract 'name'">

</select>

The above filter transforms data like [{ name: 'Bruce' }, { name: 'Chuck' }] into ['Bruce', 'Chuck'] so it becomes properly formatted.

Static Default Option

0.12.10+ only

You can provide one static default option in addition to the dyanmically generated options:

1
2
3
<select v-model="selectedUser" options="users">
<option value="">Select a user...</option>
</select>

Dynamic options created from users will be appended after the static option. The static option will be selected by default if the v-model value is falsy (excluding 0).

Input Debounce

The debounce param allows you to set a minimum delay after each keystroke before the input’s value is synced to the model. This can be useful when you are performing expensive operations on each update, for example making an Ajax request for type-ahead autocompletion.

1
<input v-model="msg" debounce="500">

Result

{{msg}}

Note that the debounce param does not debounce the user’s input events: it debounces the “write” operation to the underlying data. Therefore you should use vm.$watch() to react to data changes when using debounce. For debouncing real DOM events you should use the debounce filter.

Next: Computed Properties.