Custom Directives

25/27

Lectura

Además de las directivas de Vue que ya conoces (v-if, v-for, etc), este framework nos permite crear nuestras propias directivas personalizadas (custom directives). En esta entrada, vamos a ver qué son, cómo crearlas y cómo usarlas.

📗 https://es.vuejs.org/v2/guide/syntax.html#Directivas

Las directivas son pequeños atributos que podemos usar en elementos HTML. Todas tienen el prefijo v- para que Vue sepa que se trata de un comando especial y para mantener la coherencia.
Por lo general, son útiles si necesitas controlar comportamientos de bajo nivel en elementos del DOM.

En esta ocasión vamos a crear 2 directivas muy sencillas que nos van a ayudar a entender los conceptos básicos.

Al inicio del curso, creamos una carpeta llamada /directives. Es ahí donde van a estar nuestras directivas personalizadas. Para empezar, creamos, dentro de esta carpeta, un archivo llamado index.js. Debería estar en esta ruta:

📂 /src
└──📂 /directives
   └── index.js

De momento, el contenido que va a tener este archivo es el siguiente:

import Vue from 'vue'

Ahora, vamos a ir a la vista principal Home y abrimos el componente del formulario (src/views/Home/HomeForm.vue).

Dentro, tenemos que buscar el input en el que escribimos el BattleTag del usuario. Este es el código del input que necesitas encontrar:

<b-form-input id="input-text" v-model="form.battleTag" type="text" size="lg" required placeholder="BattleTag" />

La principal funcionalidad que tiene esta página de Home es el formulario. Por lo tanto, estaría bien que cuando un usuario llegue a esta vista el input reciba el foco (.focus(), https://developer.mozilla.org/es/docs/Web/API/HTMLElement/focus) y podamos escribir en la caja de texto directamente.

Vamos a agregar la directiva personalizada v-focus a nuestro input. Antes del id le agregamos la directiva y lo dejamos así:

<b-form-input v-focus id="input-text" v-model="form.battleTag" type="text" size="lg" required placeholder="BattleTag" />

Ahora, con el servidor levantado, vamos al navegador y abrimos la consola.

v-focus-err

Hay un error, nos dice que no entiende la directiva focus. Y es lógico, aún no hemos creado ninguna directiva.

Las directivas, al igual que el resto de elementos que hemos ido creando en la aplicación, pueden ser declaradas a nivel global o a nivel local, es decir, a nivel de componente. Para crear una directiva global, lo que vamos a hacer es usar Vue.directive().

Volvemos al archivo de las directivas que acabamos de crear, /directives/index.js y agregamos lo siguiente:

Vue.directive('focus', {})

Ya tenemos la directiva creada, fíjate que no hemos puesto el prefijo v-, pues esto lo hace automáticamente Vue.
El primer parámetro, focus es el nombre de nuestra directiva y el segundo parámetro, {} es el objeto de opciones vacío. Es decir, esta directiva no hace nada.

Tenemos declarada una directiva pero la instancia principal de Vue no sabe de su existencia. Vamos a usarla desde el archivo main.js.

import Vue from 'vue'

// BootstrapVue
import './plugins/bootstrapVue'

// Vue Font-Awesome
import './plugins/fontAwesome'

// Custom directives
import './directives'

Vamos a poner las directivas debajo de FontAwesome. Importamos el fichero de directivas que acabamos de crear. Dejamos el resto como estaba.

Ahora, si volvemos al navegador, recargamos la página y abrimos la consola, vemos que no hay ningún error. ¡Estupendo! Directiva funcionando 👌.

📗 Revisa esta documentación antes de continuar: https://es.vuejs.org/v2/guide/custom-directive.html

Vamos a darle contenido a la directiva. Tenemos que conseguir que el input que tiene la directiva reciba el foco. El código que tiene la directiva es tan sencillo como esto:

// Registra una directiva global llamada `v-focus`
Vue.directive('focus', {
  /**
   * inserted hook: Cuando el elemento es insertado en el DOM
   * @param el {HTMLElement} El elemento al que está dirigida la directiva
   */
  inserted: function (el) {
    // Enfoca el elemento
    el.focus()
  }
})

Al poner esto en funcionamiento, si recargas la página, deberías tener el input con el foco, listo para escribir:
focus

Este es el ejemplo que viene en la documentación de las directivas personalizadas con Vue, pero es perfecto para nuestro caso.

Ahora, cada vez que tengas un formulario al que le quieras aplicar esta funcionalidad, ya sabes como hacerlo.

Vamos a crear otra directiva, un poco más compleja. Agregamos lo siguiente al fichero de directivas:

Vue.directive('diablo', {})

Es importante que revises estos 2 temas antes de pasar al siguiente punto:

Vamos a crear una directiva para usarla en los títulos (<h1>). Esta directiva, que la llamaremos v-diablo va a transformar el estilo del componente.
Como es una directiva más compleja, tiene múltiples formas de ser usada. Estas son las modificaciones que va a tener:

  • De manera obligatoria, va a poner la tipografía de DiabloHeavy sobre el elemento que recibe la directiva. Para esto no hay que hacer nada especial, solo usar la directiva.
  • Las directivas soportan argumentos a través de los dos puntos :. Es decir, que si le quiero pasar un argumento a la directiva v-diablo lo que tendria que hacer es esto: v-diablo:platzi. En este caso el argumento es platzi.
  • Aparte de los argumentos, tendremos modificadores, que se usan con el punto simple (.) Un ejemplo sería así: v-diablo.top

Una cosa que hay que tener en cuenta es que las directivas no son reactivas, por lo que tendrás que recargar la web manualmente cada vez que hagas un cambio.

No necesitamos esperar a que el elemento esté cargado en el DOM, por lo cual, para esta directiva en vez de usar el hook inserted, vamos a usar el hook bind, en donde vamos a establecer la configuración inicial.

La función quedaría de la siguiente forma:

Vue.directive('diablo', {
  /**
   * @param el {HTMLElement} Elemento al que aplica la directiva
   * @param binding {DirectiveBinding} Datos que recibe la directiva a traves de argumentos, modificadores, etc
   */
  bind (el, binding) {
    // Definimos los colores
    const color = {
      bone: '#e8dcc2',
      white: '#ffffff'
    }

    // Tipografía Diablo
    el.style.fontFamily = 'DiabloHeavy, sans-serif'

    console.log('binding')
    console.log(binding)

    // Argumento (`:`)
    const arg = binding.arg === 'bone' ? 'bone' : 'white'
    // Si el argumento es 'bone'
    if (arg === 'bone') {
      // Le damos el color blanco hueso
      el.style.color = color.bone
    } else {
      // Si no, el color por defecto es el blanco
      el.style.color = color.white
    }

    // Modificadores (`.`)
    // Si hay modificadores
    if (Object.keys(binding.modifiers).length > 0) {
      // Value: (`=`)
      // Valor por defecto de 'value' es 1 (Corresponde al ancho del borde en 'px')
      const value = binding.value || 1
      // Expresión con el borde, ejemplo: '1px solid white'
      const borderExp = `${value}px solid ${color[arg]}`

      // Si el modificador es 'bottom'
      if (binding.modifiers.bottom) {
        // Ponemos una linea debajo del elemento
        el.style.borderBottom = borderExp
        // Separada 10px
        el.style.paddingBottom = '10px'
      }
      // Si el modificador es 'top'
      if (binding.modifiers.top) {
        // Ponemos una linea encima del elemento
        el.style.borderTop = borderExp
        // Separada 10px
        el.style.paddingTop = '10px'
      }
    }
  }
})
  • Argumento: Accedes a su valor a través de binding.arg. Recuerda, solo puedes pasar 1 argumento. Lo usas con los dos puntos (:). Ejemplo “v-diablo:argumento”.

  • Modificadores: Se usan con el punto (.) y puedes acceder a ellos a través de binding.modifiers, que es un objeto.

    • Si usamos esto “v-diablo.modificador1.modificador2”, en el objeto de modificadores recibimos esto:

      { modificador1: true, modificador2: true }
      
  • Valor: Lo usas con el símbolo igual (=) en la directiva y pasándole un valor, ejemplo: v-diablo="42". Accedes a su valor a través de binding.value

Argumentos posibles

Color del texto:

  • bone
  • white (color por defecto)

Modificadores posibles

Posición de la línea:

  • bottom
  • top

Valor

Grosor de la línea en px:

  • Si no le pasas valor, por defecto es 1 (px)
  • Valor numérico. Si le pasas valor, recibe el número que se le pasa (en px)

Con esto claro, tenemos varias opciones para usar nuestra directiva:

  • v-diablo:bone, color blanco hueso en el texto.
  • v-diablo:white que es lo mismo que v-diablo. El blanco es el color que hemos puesto por defecto.
  • v-diablo:bone.bottom: Le pone un border-bottom de color bone de 1px.
  • v-diablo:bone.top: Le pone un border-top de color bone de 1px.
  • v-diablo:bone.top="10": Le pone un border-top de color bone de 10px
  • v-diablo:white.top="10": Le pone un border-top de color blanco de 10px

Para el título principal de la vista Home y para el nombre del héroe, en la vista Hero, me gusta usar v-diablo:bone, para que resalte un poco más del resto de títulos.

  • /Home/HomeTitle.vue:
<h1 v-diablo:bone class="my-5">Diablo 3 Profile Finder</h1>
  • /Hero/HeroDetailHeader.vue:
<h1 v-diablo:bone class="text-truncate">{{ detail.name }}</h1>

Estos cambios se ven así de espectaculares:

home
hero

Haz pruebas y déjalo como más te guste.


Como has podido ver, tenemos infinitas posibilidades. ¿Cómo lo has usado tú? ¿Qué pasaría si usamos v-diablo:top.bottom? ¿Qué combinación te gusta más? Deja tus respuestas en el sistema de comentarios ✍️.

Aunque este ejemplo no sea el más recomendado para usar las directivas, se puede comprobar que son muy potentes en este aspecto. Tal vez para este caso hubiera sido mas recomendable hacer un componente título o simplemente agregarle las clases CSS específicas.

Si quieres ampliar conocimientos, te recomiendo que visites este enlace con muchas directivas para que puedas experimentar y probar ya mismo: https://codesandbox.io/s/directivas-personalizadas-en-vue-puyyq

¡Awesome! Ya tenemos la app a tope de power, lista para subirla y desplegarla en algún servidor.

awesome

En la siguiente clase veremos cómo subir y desplegar automáticamente nuestra aplicación.

Aportes 4

Preguntas 0

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

Genial! Lo entendí todo, lástima que la página de Vue en español esté caída ahora mismo jaja:(

Tengo entendido que dentro de las directivas personalizadas es muy útil usar el clásico DOM, es decir, puedo hacer el.querySelectorAll() en caso de que quiera modificar los elementos que contenga cierto componente que use mi directiva:D

Ussh este curso esta potente es la primera vez que siento que hice un proyecto grande y poderoso, gracias por este curso esta buenisimo, lo unico malo de que el curso sea en texto es no poder ver los gestos o la actitud del profesor expresada con el cuerpo y la voz, se que con el texto se puede lograr pero mi imaginacion no ayuda

Muy bueno y entendible el curso!