Directivas Personalizadas en Vue: Creación y Uso Básico
Clase 25 de 27 • Curso Avanzado de Vue.js 2
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.
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:
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:
- Al igual que los componentes, las directivas también tienen hooks: https://es.vuejs.org/v2/guide/custom-directive.html#Funciones-Hook
- Estos son los argumentos que soportan dichas funciones (hooks): https://es.vuejs.org/v2/guide/custom-directive.html#Argumentos-en-Hooks
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 directivav-diablo
lo que tendria que hacer es esto:v-diablo:platzi
. En este caso el argumento esplatzi
. - 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 debinding.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 debinding.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 quev-diablo
. El blanco es el color que hemos puesto por defecto.v-diablo:bone.bottom
: Le pone unborder-bottom
de colorbone
de1px
.v-diablo:bone.top
: Le pone unborder-top
de colorbone
de1px
.v-diablo:bone.top="10"
: Le pone unborder-top
de colorbone
de10px
v-diablo:white.top="10"
: Le pone unborder-top
de color blanco de10px
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:
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.
En la siguiente clase veremos cómo subir y desplegar automáticamente nuestra aplicación.