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,
message
se actualiza automáticamente. - Si
message
cambia 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
message
se 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 propmodelValue
y emite un eventoupdate:modelValue
cuando el valor cambia. - El componente padre usa
v-model
para enlazarmessage
con el componente hijo.