Creación de Componentes Personalizados con VModel en Vue.js

Clase 19 de 23Curso de Vue.js: Introducción y Fundamentos

Contenido del curso

Resumen

Construir componentes reutilizables que se comuniquen de forma bidireccional con su componente padre es una de las habilidades más valiosas al trabajar con Vue.js. Cuando dominas la combinación de props, eventos personalizados y v-model, puedes diseñar piezas de interfaz que se comportan exactamente igual que los elementos nativos de HTML, pero con toda la potencia de la reactividad de Vue.

¿Cómo fluyen los datos del componente padre al hijo con props?

El punto de partida es tener un componente padre que defina una variable en su función data y un componente hijo que la reciba. En el ejemplo se crea un componente llamado v-input que contiene un <input type="text"> nativo [0:42]. Desde el componente raíz se declara una variable text con el valor "Hola, Vue" y se envía al hijo mediante v-bind con el nombre de prop value [1:30].

Dentro del componente hijo, la propiedad se declara en el objeto props —es importante recordar que el atributo se llama props en plural [2:18]—. Para que el valor aparezca dentro del input, se enlaza con :value="value", aprovechando que los inputs de HTML ya poseen un atributo value nativo [2:42].

  • El padre envía el dato con v-bind.
  • El hijo lo recibe como prop y lo asigna al input.
  • Cualquier cambio en el hijo no se propaga automáticamente al padre.

¿Cómo emitir cambios del hijo al padre con eventos personalizados?

Para que el componente padre se entere de las modificaciones, el hijo debe emitir un evento cada vez que el usuario escriba. Se utiliza v-on:input en el <input> del hijo, que ejecuta un method encargado de leer event.target.value [3:28].

Dentro de ese method, en lugar de solo imprimir el valor en consola, se llama a this.$emit pasando un nombre de evento y el texto actual [4:08]. El padre escucha ese evento con v-on y actualiza su variable text.

js // Componente hijo methods: { input(ev) { this.$emit('update', ev.target.value) } }

Un detalle importante: si el nombre del evento coincide con uno que Vue.js ya utiliza internamente (como input), pueden surgir conflictos. Renombrarlo a algo como update resuelve el problema [5:10].

¿Por qué la comunicación bidireccional requiere ambos mecanismos?

Sin el prop, el hijo no recibe el valor inicial. Sin el emit, el padre nunca se entera de los cambios. Ambos canales —v-bind hacia abajo y v-on hacia arriba— son indispensables para lograr la sincronización completa entre componentes.

¿Cómo simplificar todo con v-model en componentes personalizados?

Vue.js ofrece una sintaxis abreviada que reemplaza la combinación de v-bind y v-on por una sola directiva: v-model [5:58]. La regla es sencilla:

  • En el consumo del componente, se escribe v-model:value="text" en lugar de definir prop y event handler por separado.
  • El event handler del padre ya no es necesario porque Vue actualiza la variable de forma automática.
  • En el hijo, el evento emitido debe llamarse update: seguido del nombre del prop, por ejemplo update:value [6:52].

js // Componente padre (consumo) <v-input v-model:value="text"></v-input>

js // Componente hijo (emisión) this.$emit('update:value', ev.target.value)

¿Qué ventajas ofrece esta convención?

  • Código más corto: se eliminan los methods intermedios en el padre.
  • Legibilidad: quien consuma el componente ve de inmediato que hay enlace bidireccional.
  • Flexibilidad: el nombre del prop puede ser cualquiera; solo hay que mantener la coherencia entre el prop, el v-model y el update:nombreDelProp [7:18].

Esta misma técnica funciona con cualquier tipo de dato: cadenas de texto, booleanos, números, objetos JSON o listas. Así, puedes diseñar selects, checkboxes, componentes de formulario complejos y cualquier pieza de UI que necesite reactividad completa.

Si ya pusiste en práctica el ejemplo, intenta crear un componente personalizado con un tipo de dato diferente —por ejemplo, un booleano con un checkbox— y comparte tu resultado en los comentarios para recibir retroalimentación.