En Vue.js, la directiva v-model permite crear un binding bidireccional entre un elemento de formulario HTML y una variable de JavaScript en la instancia de Vue.
El binding bidireccional funciona en ambos sentidos, desde los datos en el JavaScript al HTML y viceversa.
Es decir,
- Cualquier cambio en el elemento de formulario ➡️ se reflejará en los datos
- Cualquier cambio en los datos ➡️ se reflejará en el elemento de formulario.
En el tutorial anterior vimos el binding unidireccional, de atributos, que funciona sólo desde JS a HTML, y se realiza con la directiva v-bind.
Directiva v-model
Como decíamos la directiva v-model permite hacer un binding bidireccional que funciona entre los datos del JavaScript y al HTML.
El enlace ocurre entre un elemento de formulario (como <input>, <textarea>, o <select>), que son los elementos que pueden recibir una entrada de datos por el usuario.
El binding con v-model permite que los cambios en el elemento de formulario se reflejen automáticamente en los datos, y viceversa.
<template>
<input v-model="message" placeholder="Escribe algo" />
<p>Mensaje: {{ message }}</p>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('');
</script>En este ejemplo:
- El valor del campo de texto está enlazado a la variable
message. - Si el usuario escribe en el campo,
messagese actualiza automáticamente. - Si
messagecambia en el script, el campo de texto se actualiza automáticamente.
Bajo el capó v-model
<input v-model="message">Básicamente es lo mismo que un binding de value y un handler para input
<input :value="message" @input="event => message = event.target.value">Uso de v-model con diferentes elementos de formulario
El v-model se comporta de manera (ligeramente) diferente según el tipo de elemento de formulario al que lo apliquemos.
En función del elemento en el que lo usemos, será,
| Elemento | Atributo vinculado | Tipo de dato | Comportamiento |
|---|---|---|---|
<input> (texto) | value | String | El valor del input se vincula a la propiedad. Actualiza bidireccionalmente. |
<textarea> | value | String | El contenido del textarea se vincula a la propiedad. Actualiza bidireccionalmente. |
<select> | value de la opción seleccionada | String (o array) | El valor seleccionado se vincula a la propiedad. En multiple, es un array. |
<input> (checkbox) | checked | Boolean (o array si es multiple) | El estado de marcado se vincula a la propiedad. En multiple, es un array. |
<input> (radio) | checked | String | El valor de la opción seleccionada se vincula a la propiedad. Solo una opción puede estar seleccionada. |
Vue maneja el vínculo de manera adecuada para asegurar que los datos se actualicen correctamente en ambas direcciones.
<template>
<input v-model="text" type="text" placeholder="Escribe algo" />
<p>Texto ingresado: {{ text }}</p>
</template>
<script setup>
import { ref } from 'vue';
const text = ref('');
</script><template>
<textarea v-model="description" placeholder="Escribe una descripción"></textarea>
<p>Descripción: {{ description }}</p>
</template>
<script setup>
import { ref } from 'vue';
const description = ref('');
</script><template>
<select v-model="selectedOption">
<option value="opcion1">Opción 1</option>
<option value="opcion2">Opción 2</option>
<option value="opcion3">Opción 3</option>
</select>
<p>Opción seleccionada: {{ selectedOption }}</p>
</template>
<script setup>
import { ref } from 'vue';
const selectedOption = ref('opcion1');
</script><template>
<label>
<input type="checkbox" v-model="isChecked" />
¿Estás de acuerdo?
</label>
<p>Estado del checkbox: {{ isChecked ? 'Sí' : 'No' }}</p>
</template>
<script setup>
import { ref } from 'vue';
const isChecked = ref(false);
</script>Modificadores de v-model
Los modificadores de v-model permiten ajustar el comportamiento de la sincronización de datos entre el estado y el DOM en Vue.js.
| Modificador | Descripción |
|---|---|
.lazy | Actualiza el valor solo después de que ocurra el evento change |
.number | Convierte automáticamente el valor de la entrada en un número |
.trim | Elimina los espacios en blanco al principio y al final del valor. |
El modificador .lazy hace que v-model actualice el valor solo después del evento change (en lugar de input).
<template>
<input v-model.lazy="message" placeholder="Escribe algo" />
<p>Mensaje: {{ message }}</p>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('');
</script>En este ejemplo:
- El valor de
messagese actualiza solo cuando el campo de texto pierde el foco.
El modificador .number convierte automáticamente la entrada a un número.
<template>
<input v-model.number="age" type="number" placeholder="Edad" />
<p>Edad: {{ age }} ({{ typeof age }})</p>
</template>
<script setup>
import { ref } from 'vue';
const age = ref(0);
</script>En este ejemplo:
- La entrada se convierte automáticamente a un número.
El modificador .trim elimina espacios en blanco al principio y al final de la entrada.
<template>
<input v-model.trim="username" placeholder="Nombre de usuario" />
<p>Nombre de usuario: "{{ username }}"</p>
</template>
<script setup>
import { ref } from 'vue';
const username = ref('');
</script>En este ejemplo:
- Los espacios en blanco al principio y al final se eliminan automáticamente.
Es posible combinar varios modificadores para lograr un comportamiento más preciso.
<input v-model.lazy.trim.number="amount" />Uso de v-bind en nuestros componentes Avanzado
v-model también se puede usar en nuestros propios componentes para crear un enlace bidireccional entre el componente padre y el componente hijo.
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>Mensaje: {{ message }}</p>
</template>
<script setup>
import { ref } from 'vue';
import CustomInput from './CustomInput.vue';
const message = ref('');
</script>En este ejemplo:
- El componente hijo (
CustomInput) recibe la propmodelValuey emite un eventoupdate:modelValuecuando el valor cambia. - El componente padre usa
v-modelpara enlazarmessagecon el componente hijo.