Como crear componentes reusables en VueJS 3

Como crear componentes reusables en VueJS 3

Hola!!! En esta entrega quiero compartir con ustedes como crear componentes reusables en Vue 3. Muchas veces a la hora de comenzar un desarrollo no nos planteamos la estructura del proyecto y vamos creando componentes según vamos desarrollando, por eso es bien importante tomarse un tiempo de planificación para entre otras cosas definir los componentes que necesitamos reutilizar.

Para este ejemplo he creado un proyecto Vue3 con Vite y vamos desarrollar un componente reutilizable para procesar los datos de una tarjeta de crédito con diferentes lógica de negocio. El código de este proyecto está disponible en GitHub para que los puedas testear.

Un error que cometemos a menudo cuando estamos creando un componente reutilizable es escribir la lógica de negocio dentro del componente por lo que a medida que comenzamos a utilizar este componente comienzan a surgir nuevos casos de uso y puede llegar el momento en que se haga un poco engorroso implementar el componente. Por lo tanto, es bien importante definir los componentes que vamos a reutilizar y mantener la lógica de negocio fuera del componente.

Estructura del proyecto

Como podemos ver esto es un proyecto muy sencillo, donde solo he creado el componente PaymentCard.vue, no voy a entrar en los detalles de estilos y es importante aclarar que es necesario estar familiarizado con los conceptos básicos de Vue 3.

image.png

Componente padre

Aquí tenemos el App.vue, donde vamos a renderizar nuestro componente reusable.

<script setup>
import { ref } from 'vue'   

// 1. Importamos el componente
import PaymentCard from './components/PaymentCard.vue';

// 2. Variable reactiva que guardará los datos de la tarjeta
const dataPayment = ref({})

// 3. Función que procesará el pago
const processDataPayment = () =>  {
  console.log('Procesamos el pago')
  // const response = await myapi.post(dataPayment)
}
</script>

<template>
  <h1>Procesar Pago</h1>
  <!--
    4. Componente reutilizable para procesar los pagos

    v-model:dataPayment="dataPayment": Pasamos los datos
    al componente a través de la propiedad v-model:dataPayment.
    v-model nos va a permitir tener sincronizados los cambios
    de la propiedad entre el componente padre (App.vue) y el
    hijo (PaymentCard)

    @processDataPayment="processDataPayment": Disparamos un
    evento en el componente hijo (PaymentCard) @processPayment
    y procesamos el pago en el componente padre (App.vue)
    con la función processPayment
  -- >
  <PaymentCard
    v-model:dataPayment="dataPayment"
    @processDataPayment="processDataPayment"
  />
</template>

Lo más importante en este punto es el uso de v-model (4) al pasar la propiedad dataPayment al componente PaymentCard, esta directiva en Vue 3 nos permite mantener sincronizados los cambios entre el componente hijo y el padre.

Por lo tanto cuando en el componente PaymentCard se dispare el evento processDataPayment se ejecutará la función processDataPayment en el componente padre y podremos procesar los datos de la variable dataPayment (2) sin ningún problema porque esta siempre estará actualizada.

Componente hijo

Ahora veamos el componente PaymentCard, voy a dividir el componente en dos secciones para una mejor lectura.

<script setup>
import { computed } from 'vue'

// 1. Definimos la propiedad que pasaremos desde
// el componente padre
const props = defineProps({
  dataPayment: {
    required: true,
    type: Object,
    default: {
      default: () => ({
        name: '',
        cardNumber: '',
        expirationDate: '',
        securityCode: '',
      })
    }
  }
})

/**
Definimos los datos y eventos que vamos a emitir al padre

2. update:dataPayment: Mantendrá actualizada la propiedad dataPayment
con el componente padre

3.processDataPayment: Es el evento que enviaremos el padre para avisar
que se puede procesar el pago
*/
const emit = defineEmits([
  'update:dataPayment',
  'processDataPayment'
])

/**
4. En Vue 3 no podemos modificar directamente el valor de una propiedad
por lo que una de las formas para resolver este problema es a través
de una computada.
En este caso con la función get() obtenemos el valor de la propiedad
dataPayment y con la función set() emitimos al padre los valores
actualizados a través del formulario.
*/
const inputs = computed({
  get: () => props.dataPayment,
  set: (value) => emit('update:dataPayment', value)
})

/** Definimos las variables que vamos a exponer al template html */
defineExpose({
  inputs,
})
</script>

<!-- El template del componente se incluye más abajo --/>

Los más importante a tener en cuenta es definir las propiedades de forma correcta y si es posible con los valores por defecto.

Definir las variables y eventos que vamos a emitir al padre, recuerda que hemos pasado la propiedad dataPayment con la directiva v-model, por lo tanto para mantenerla actualizada debemos emitirla con el parámetro update

Por último, no podemos realizar mutaciones directamente sobre una propiedad (2), por lo que una de las formas de resolver este problema es a través de una computada que se encargará de emitir los cambios que se realicen sobre esta hacia hacia el padre para mantenerla actualizada.

<template>
 <!-- 1. Emitimos el evento  processDataPayment al hacer el submit del formulario-->
  <form @submit.prevent="$emit('processDataPayment')" class="form">
    <div class="row">
      <div class="name">
        <label for="ccnum">Full Name</label>
        <input
          placeholder="Full name"
          type="text"
          size="19"
          v-model="inputs.fullName"
        >
      </div>

      <div class="car-number">
        <label for="ccnum">Card Number</label>
        <input
          placeholder="---- ---- ---- ----"
          type="text"
          size="19"
          v-model="inputs.cardNumber"
        >
      </div>

      <div class="expiration">
        <label for="expiry">Expiration</label>
        <input
          placeholder="-- / --"
          size="7"
          type="text"
          v-model="inputs.expirationDate"
        >
      </div>
      <div class="cvc">
        <label for="cvc">CVC</label>
        <input
          placeholder="---"
          size="4"
          type="text"
          v-model="inputs.cvc"
        >
      </div>
    </div>
    <div class="btn-process">
      <button type="submit">Process</button>
    </div>
  </form>
</template>

En el template emitimos el evento processDataPayment al componente padre cuando hacemos click en el botón Process

Destacar además que una buena práctica es declarar reglas de validación para evitar enviar el formulario con datos erroneos.

Como pueden ver haciendo un uso correcto de las propiedad y eventos podemos mantener la lógica de negoció fuera del componente reutilizable para que los futuros casos de uso no afecten al componente.

Si te has bajado el proyecto del repositorio puedes ejecutarlo en consola:

npm run serve

image.png

Completar los datos y al hacer click en el botón procesar veremos el mensaje en consola de la función processDataPayment

Flujo de los componentes

image.png

Bueno, espero que este pequeño tutoríal te ayude a escribir mejores componentes... Hasta la próxima.

Did you find this article valuable?

Support Roylan Suarez Reyes by becoming a sponsor. Any amount is appreciated!