Skip to content
← Todas las preguntas
⚡ Intermedio

¿Cómo funciona v-model en componentes personalizados?

🧩 Componentes📌 Directivas🔄 v-model

Cuando escribes v-model en un <input> nativo, Vue vincula su valor y escucha eventos de input. Cuando escribes v-model en un componente personalizado, Vue hace algo similar: pasa una prop modelValue y escucha un evento update:modelValue. El componente controla lo que el usuario ve y cuándo emitir actualizaciones.

Diagrama de flujo de datos para v-model con componentes personalizados usando defineModel

A qué se expande v-model

vue
<!-- Estos dos son equivalentes -->
<SearchInput v-model="query" />
<SearchInput :modelValue="query" @update:modelValue="query = $event" />
Open in Vue Playground

El padre proporciona los datos a través de una prop. El hijo emite un evento cuando los datos deberían cambiar. El padre decide si acepta el cambio. Esto preserva el flujo de datos unidireccional.

defineModel (Vue 3.4+)

defineModel es la forma moderna de implementar v-model en un componente hijo. Crea un ref que puedes leer y escribir. Vue maneja el binding de la prop y la emisión del evento internamente:

vue
<!-- SearchInput.vue -->
<script setup lang="ts">
const model = defineModel<string>()
</script>

<template>
  <input
    :value="model"
    @input="model = ($event.target as HTMLInputElement).value"
  />
</template>
Open in Vue Playground

O vincúlalo directamente con v-model en un input nativo:

vue
<template>
  <input v-model="model" />
</template>
Open in Vue Playground

Antes de Vue 3.4, tenías que declarar la prop y el emit por separado. defineModel elimina ese boilerplate.

v-models con nombre (múltiples bindings)

Un solo componente puede tener múltiples bindings v-model dando un nombre a cada uno:

vue
<!-- Padre -->
<UserForm v-model:firstName="first" v-model:lastName="last" />
Open in Vue Playground
vue
<!-- UserForm.vue -->
<script setup lang="ts">
const firstName = defineModel<string>('firstName')
const lastName = defineModel<string>('lastName')
</script>

<template>
  <input v-model="firstName" placeholder="First name" />
  <input v-model="lastName" placeholder="Last name" />
</template>
Open in Vue Playground

Cada v-model con nombre se convierte en su propio par prop/emit: :firstName + @update:firstName.

Modificadores de v-model

Vue tiene modificadores integrados (.trim, .number, .lazy), y puedes definir los tuyos propios. El padre pasa modificadores, y el hijo accede a ellos a través de defineModel:

vue
<!-- Padre -->
<SearchInput v-model.capitalize="query" />
Open in Vue Playground
vue
<!-- SearchInput.vue -->
<script setup lang="ts">
const [model, modifiers] = defineModel<string>({
  set(value) {
    if (modifiers.capitalize) {
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
    return value
  }
})
</script>

<template>
  <input v-model="model" />
</template>
Open in Vue Playground

Ver también: ¿Cómo funcionan múltiples bindings v-model? · ¿Qué son los modificadores personalizados de v-model? · ¿Por qué mutar un objeto a través de defineModel no actualiza al padre?

Referencias

Publicado bajo la licencia MIT.