The v-model directive is used to create two-way data binding between form input elements and state, and between Vue components. In this topic, we'll discuss the two-way data binding concept in more detail, see how the v-model works, and its main use cases. In particular, you'll learn how to work with different input elements, such as textarea, checkbox, radio buttons, and so on.
What is v-model?
When dealing with inputs, you need a way to keep track of every keystroke of a user and save it to the state. It's good practice to use controlled inputs, which means that an input value and a state variable are the same, so if changes happen in an input, it's automatically synced with the state and vice versa.
To bind a state with an input, we need a state variable and input event. This can be accomplished via v-bind and v-on:input, like in this example:
<script setup>
import { ref } from 'vue'
const name = ref('')
</script>
<template>
<div>{{ name }}</div>
<input v-bind="name" v-on:input="e => name = e.target.value" />
</template>Here we initialized a name variable and assigned it to an empty string, then we want to update it on the input event. If you open your browser and type your name, you should see it's the same for the input field and the div that outputs the state.
v-model is a new way to do the same with less code. It takes care of a lot of work — we only need to write v-model="stateVariable", and the rest will be handled by Vue. Here's how we can rewrite the code above:
<script setup>
import { ref } from 'vue'
const name = ref('')
</script>
<template>
<div>{{ name }}</div>
<input v-model="name" />
</template>Cool! We didn't have to write the event listener for input change, Vue picked it up itself. Check it out in your browser — it should work just like it did with v-bind.
To sum up, v-bind offers one-way data binding, which means that changes in a state variable will be reflected in the view (template), but the view can't change the state. v-model, on the other hand, allows two-way data binding, which means that updates can come both from the state and the template.
V-model with a textarea
v-model works exactly the same with the textarea element. You just need to initialize a state in the script tag and link it up with the textarea via v-model. Take a look:
<script setup>
import { ref } from 'vue';
const about = ref('')
</script>
<template>
<div>{{ about }}</div>
<textarea v-model="about" placeholder="Tell me about yourself..."></textarea>
</template>Open the page in the browser and type something into the textarea field. You should see the same text output above the textarea. This means we've successfully bound the HTML element with the state.
V-model with a checkbox
Working with checkboxes is pretty easy too. In plain HTML a checkbox has a checked attribute that you can toggle from true to false. In Vue.js we don't provide that attribute. Instead, v-model can handle this and update the state of the checkbox when you check/uncheck the element. Here's a visual example:
<script setup>
import { ref } from 'vue';
const checkbox = ref(true)
</script>
<template>
<div>{{ checkbox }}</div>
<input type="checkbox" v-model="checkbox"/>
</template>If you check the browser, the checkbox will be checked, because it's the initial value that we set inside the script tag. Now if you try to check/uncheck the checkbox, you'll see that it switches between true and false.
Let's take this checkbox logic to the next level! Instead of just showing "true" or "false", it'd be cool to assign some values to these booleans and output them based on the checkbox status. You can add true-value and false-value inside the input tag to achieve this:
<script setup>
import { ref } from 'vue';
const answer = ref('Yes!')
</script>
<template>
<label> Do you like Vue.js?
<input type="checkbox" v-model="answer" true-value="Yes!" false-value="No" />
</label>
<div>{{ answer}}</div>
</template>You can provide any value to the true-value and false-value attributes. When the checkbox is checked, the answer's value will be set to "Yes!", and it will be set to "No" if it's unchecked:
Remember that true-value and false-value only work with v-model.
Imagine you have a form where users can check multiple checkboxes. In this case, we can provide the value attribute for every checkbox and save all checked values inside an array. Below there's a form that lets users choose what languages they speak. They can choose more than one, and the data will be displayed on the screen:
<script setup>
import { ref } from 'vue';
const langs = ref([])
</script>
<template>
<div>I speak: {{ langs }}</div>
<label><input type="checkbox" v-model="langs" value="English" />English</label>
<label><input type="checkbox" v-model="langs" value="Spanish" />Spanish</label>
<label><input type="checkbox" v-model="langs" value="Russian" />Russian</label>
<label><input type="checkbox" v-model="langs" value="Turkish" />Turkish</label>
</template>Output:
V-model with a radio button
Radio buttons are used in a situation where we want to give a user a choice between related options. Each radio button has its value attribute, and we can identify what value was selected by using v-model. Check out this example:
<script setup>
import { ref } from 'vue';
const food = ref('sushi')
</script>
<template>
<div>Favorite food: {{ food }}</div>
<label><input type="radio" v-model="food" value="sushi" />Sushi</label>
<label><input type="radio" v-model="food" value="pizza" />Pizza</label>
<label><input type="radio" v-model="food" value="kebab" />Kebab</label>
</template>There are three inputs and each of them is bound to the food state variable via v-model. When you click one of these buttons, the v-model takes the button's value and saves it to the state. If we click on Kebab, this is what we get:
V-model with select
When dealing with select options, it's enough to provide v-model inside the select tag, and it's not necessary to specify values for every option. Vue will look inside the option tag that you've clicked and pull out its value. Here's a simple voting app:
<script setup>
import { ref } from "vue";
const vote = ref("");
</script>
<template>
<div>I vote for: {{ vote }}</div>
<select v-model="vote">
<option>Mike Peterson</option>
<option>John Doe</option>
<option>Nick Smith</option>
</select>
</template>Here is the result:
We've just covered the main form inputs and how they work with v-model. Some of them work similarly, others differ a bit, but no worries if you forget about how a specific input works — you can always look it up here or in Vue.js Docs.
V-model modifiers
There are three modifiers that we're going to discuss here: lazy, trim and number.
Lazy
The lazy modifier really comes in handy if you want to keep your app optimized and avoid unnecessary state updates. What this modifier does is change the input event to change. The input event updates the state on every keystroke, whereas the latter waits until you focus out from the input field, and only then saves the value to the state. You will probably use this modifier pretty often. Here's the syntax: you just add .lazy to the v-model.
<script setup>
import { ref } from "vue";
const name = ref("");
</script>
<template>
<div>My name is {{ name }}</div>
<input v-model.lazy="name" />
</template>Copy this code and try to type something — you'll notice that the state doesn't update on every keystroke, but if you click outside the field, you'll see that the string inside the div changes.
Trim
This one is easy and is similar to JS's native trim() method. As the name suggests, it trims the whitespace from the start and end, and only then saves it to the state. Here's how you write it:
<input v-model.trim="name" />Number
Just like JS's Number() method, the number modifier converts a string to the number type. If the string, for example, is equal to "25", the modifier will turn it into 25 of type number. However, if the input value is some sort of alphabetical string like "Hello", it will remain the same. Here's the syntax:
<input v-model.number="age" />Conclusion
That was our quick overview of the v-model directive. The v-model directive allows syncing inputs' values to the state. It facilitates data binding and can be used as a shorter alternative to v-bind + v-on. In this topic, we covered the most common use cases for v-model and learned about three modifiers that can help you optimize and customize the way you handle inputs.