vuejs-vmodel-binding-datos

Data binding with `v-model` in Vue.js

  • 5 min

In Vue.js, the v-model directive allows you to create a bidirectional binding between an HTML form element and a JavaScript variable in the Vue instance.

The bidirectional binding works both ways, from the data in JavaScript to HTML and vice versa.

That is,

  • Any change in the form element ➡️ will be reflected in the data
  • Any change in the data ➡️ will be reflected in the form element.

v-model Directive

As we mentioned, the v-model directive allows for bidirectional binding that works between JavaScript data and HTML.

The binding occurs between a form element (such as <input>, <textarea>, or <select>), which are the elements that can receive user input.

The binding with v-model allows changes in the form element to automatically reflect in the data, and vice versa.

<template>
  <input v-model="message" placeholder="Type something" />
  <p>Message: {{ message }}</p>
</template>

<script setup>
import { ref } from 'vue';

const message = ref('');
</script>

In this example:

  • The value of the text field is bound to the variable message.
  • If the user types in the field, message is automatically updated.
  • If message changes in the script, the text field is automatically updated.

Using v-model with Different Form Elements

The v-model behaves (slightly) differently depending on the type of form element to which we apply it.

Depending on the element we use it on, it will be,

ElementBound AttributeData TypeBehavior
<input> (text)valueStringThe input value is bound to the property. Updates bidirectionally.
<textarea>valueStringThe content of the textarea is bound to the property. Updates bidirectionally.
<select>value of the selected optionString (or array)The selected value is bound to the property. In multiple, it is an array.
<input> (checkbox)checkedBoolean (or array if multiple)The checked state is bound to the property. In multiple, it is an array.
<input> (radio)checkedStringThe value of the selected option is bound to the property. Only one option can be selected.

Vue handles the binding correctly to ensure that the data is updated properly in both directions.

<template>
  <input v-model="text" type="text" placeholder="Type something" />
  <p>Entered text: {{ text }}</p>
</template>

<script setup>
import { ref } from 'vue';

const text = ref('');
</script>
<template>
  <textarea v-model="description" placeholder="Write a description"></textarea>
  <p>Description: {{ description }}</p>
</template>

<script setup>
import { ref } from 'vue';

const description = ref('');
</script>
<template>
  <select v-model="selectedOption">
    <option value="option1">Option 1</option>
    <option value="option2">Option 2</option>
    <option value="option3">Option 3</option>
  </select>
  <p>Selected option: {{ selectedOption }}</p>
</template>

<script setup>
import { ref } from 'vue';

const selectedOption = ref('option1');
</script>
<template>
  <label>
    <input type="checkbox" v-model="isChecked" />
    Do you agree?
  </label>
  <p>Checkbox status: {{ isChecked ? 'Yes' : 'No' }}</p>
</template>

<script setup>
import { ref } from 'vue';

const isChecked = ref(false);
</script>

v-model Modifiers

The v-model modifiers allow you to adjust the behavior of data synchronization between the state and the DOM in Vue.js.

ModifierDescription
.lazyUpdates the value only after the change event occurs
.numberAutomatically converts the input value to a number
.trimRemoves whitespace from the beginning and end of the value.

The .lazy modifier makes v-model update the value only after the change event (instead of input).

<template>
  <input v-model.lazy="message" placeholder="Type something" />
  <p>Message: {{ message }}</p>
</template>

<script setup>
import { ref } from 'vue';

const message = ref('');
</script>

In this example:

  • The value of message is updated only when the text field loses focus.

The .number modifier automatically converts the input to a number.

<template>
  <input v-model.number="age" type="number" placeholder="Age" />
  <p>Age: {{ age }} ({{ typeof age }})</p>
</template>

<script setup>
import { ref } from 'vue';

const age = ref(0);
</script>

In this example:

  • The input is automatically converted to a number.

The .trim modifier removes whitespace from the beginning and end of the input.

<template>
  <input v-model.trim="username" placeholder="Username" />
  <p>Username: "{{ username }}"</p>
</template>

<script setup>
import { ref } from 'vue';

const username = ref('');
</script>

In this example:

  • Whitespace from the beginning and end is automatically removed.

It is possible to combine multiple modifiers for more precise behavior.

<input v-model.lazy.trim.number="amount" />

Using v-bind in Our Components Advanced

v-model can also be used in our own components to create a bidirectional binding between the parent component and the child component.

CustomInput.vue

<template>
  <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>

<script setup>
defineProps({
  modelValue: String,
});

defineEmits(['update:modelValue']);
</script>

ParentComponent.vue

<template>
  <CustomInput v-model="message" />
  <p>Message: {{ message }}</p>
</template>

<script setup>
import { ref } from 'vue';
import CustomInput from './CustomInput.vue';

const message = ref('');
</script>

In this example:

  • The child component (CustomInput) receives the prop modelValue and emits an update:modelValue event when the value changes.
  • The parent component uses v-model to bind message with the child component.