Hasta ahora, conectar de forma reactiva tus componentes con v-model
era un proceso un tanto manual. Tienes que definir las propiedades junto a los eventos, y no olvidarte de emitirlos usando la sintaxis correcta.
Un ejemplo sencillo:
<script setup>
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', ($event.target as HTMLInputElement).value)"
/>
</template>
✋ defineModel
Desde Vue 3.4, tenemos acceso al macro defineModel
, que simplifica el proceso sobremanera. Por ejemplo, podemos replicar el ejemplo anterior usando solo defineModel
.
<script setup>
const model = defineModel();
</script>
<template>
<input v-model="model" />
</template>
defineModel
se encarga por nosotros de crear la propiedad modelValue
, el evento update:modelValue
y unirlo todo en compile time. Por lo que al final el código resultante es parecido al primero ejemplo.
La propiedad que creamos es una referencia (ref
) reactiva común, que está sincronizada en dos direcciones: padre e hijo.
<script setup>
...
const data = ref('defineModel rocks');
</script>
<template>
<Child v-model="data" />
</template>
Aún al hacerlo a través de una macro, estamos declarando una propiedad, así que también podemos especificar las opciones más usuales:
const model = defineModel({ default: 'Foo', required: true, type: String });
Más de un v-model
Si quieres usar más de un v-model
tendrás que utilizar un identificador a través de un argumento: v-model:rating
, por ejemplo. En este caso, basta con usar el identificador al usar la macro.
Repitamos el mismo ejemplo:
<script setup>
...
const title = ref('defineModel rocks');
const rating = ref(5);
</script>
<template>
<Child v-model:title="title" v-model:rating="rating" />
</template>
Cada propiedad pasada con v-model
al componente hijo, se sincroniza con el padre como de costumbre, pero al usar defineModel
todo el proceso es menos verbose.
<script setup>
const model = defineModel();
const rating = defineModel('rating', { required: true });
</script>
<template>
<input type="text" v-model="model" />
<input type="number" v-model="rating" />
</template>
Modificadores de v-model
En caso de que quieras crear tu propio modificador de v-model
, puedes seguir usando defineModel
. Supongamos que quieres añadir el modificador reverse
para darle la vuelta a valor de v-model
. Como podrás imaginar el proceso es mucho más sencillo, ya que no tenemos que crear una función que aplique la transformación que buscamos. defineModel
se encarga de todo.
En el componente hijo ya basta con que desestructures su resultado y uses los métodos get
y set
(como en las propiedades computadas).
<script setup>
const [title, titleModifiers] = defineModel('title', {
set(value) {
if (titleModifiers.reverse) {
return value.split('').reverse('').join('');
}
return value;
},
});
...
</script>
Conclusión
La nueva macro defineModel
puede parecer trivial, pero cualquiera que tenga que trabajar con múltiples bindings entre componentes sabrá lo tedioso que es mantener todo actualizado.
De esta forma e igual que con la sintaxis setup
, Vue nos hace la vida un poco más sencilla.