Objetivo y requisitos
Vamos a crear un input que sincroniza su valor con el padre y transforma el texto cuando recibe el modificador .capitalize. Necesitas Vue 3.4 o superior y un proyecto Vue con Vite. defineModel es una macro de <script setup>: no se importa desde Vue.
El componente completo: CustomInput.vue
Guarda este archivo en src/components/CustomInput.vue. Hay una sola llamada a defineModel para el modelo predeterminado. El primer valor es una referencia y el segundo contiene los modificadores recibidos del padre.
<script setup lang="ts">
const [model, modifiers] = defineModel<string, 'capitalize'>({
required: true,
set(value) {
if (!modifiers.capitalize) return value
return value.charAt(0).toUpperCase() + value.slice(1)
},
})
</script>
<template>
<label>
Nombre
<input v-model="model" type="text" autocomplete="name" />
</label>
</template>
El set transforma el valor al escribir en el modelo. El componente padre recibirá el texto con la primera letra en mayúscula. Sin .capitalize, el valor se transmite sin esa transformación.
Uso desde App.vue
<script setup lang="ts">
import { ref } from 'vue'
import CustomInput from './components/CustomInput.vue'
const name = ref('')
</script>
<template>
<CustomInput v-model.capitalize="name" />
<p>Valor en el padre: {{ name }}</p>
</template>
Resultado esperado
Ejecuta npm run dev y escribe aldair. El párrafo del padre debe mostrar Aldair. Quita .capitalize, vuelve a escribir y comprueba que el párrafo conserva el texto original. Finalmente, ejecuta npm run build para verificar que el componente compila.
Modelos con nombre
Si necesitas varios valores, cada modelo debe tener un nombre diferente. Este ejemplo es una alternativa independiente al input anterior:
<script setup lang="ts">
const firstName = defineModel<string>('firstName', { required: true })
const lastName = defineModel<string>('lastName', { required: true })
</script>
<template>
<label>Nombre <input v-model="firstName" /></label>
<label>Apellido <input v-model="lastName" /></label>
</template>
Desde el padre se usan v-model:first-name y v-model:last-name, cada uno con su propia referencia inicializada.
Errores frecuentes
No declares dos llamadas sin nombre a defineModel dentro del mismo componente: ambas intentarían definir modelValue. Tampoco supongas que un modificador personalizado transforma el valor por sí solo; su comportamiento se implementa en get o set. Los modificadores de inputs nativos y los de componentes tienen reglas distintas.
Un default en el hijo puede dejarlo desincronizado si el padre mantiene su referencia en undefined. En este ejemplo inicializamos name y exigimos el modelo con required: true. Para otros contratos, decide expresamente cuál será el valor inicial compartido.
Consulta la documentación oficial de v-model en componentes para modelos con nombre, modificadores y compatibilidad con versiones anteriores.
Sigue leyendo
Una idea lleva a otra.
)
)
)
)