Hasta ahora hemos visto cómo crear y declarar nuestras directivas personalizadas de forma global pero en este ejercicio podremos ver cómo se definen de forma local dentro de una instancia o componente Vue.
Para ello simplemente tendremos que ir al código javascript de nuestra instancia y añadir un nuevo objeto directives con una sintaxis ligeramente diferente a la que hemos visto hasta ahora.
En este ejemplo condensaremos varias de las directivas de los ejercicios anteriores añadiéndolas en este apartado con sus correspondientes funciones.
En la declaración no cambia nada:
<h1 v-color:yellow v-format.bold.underline.highlight>{{message}}</h1>
Y el código JS quedaría de la siguiente forma:
directives: {
‘white’: {
bind(el, binding, vnode) {
el.style.color = ‘#fff’;
}
},
‘color’: {
bind(el, binding, vnode) {
el.style.color = binding.arg;
}
},
‘format’: {
bind(el, binding, vnode) {
const modifiers = binding.modifiers;
if (modifiers.underline) {
el.style.textDecoration = “underline”;
}
…
},
}
}
});
Tendríamos un resultado similar [1] al de ejercicios anteriores [2].
IMPORTANTE
La nomenclatura del componente es importante, debe ser de tipo camel-case “MiComponente” o kebab-case “mi-componente”, en definitiva, una forma de unir palabras.
Los componentes en Vue son unos contenedores donde implementamos lógica para que funcionen de manera aislada y puedan ser reutilizados por la aplicación.
Creemos un componente donde, al escribir una palabra, se muestre su traducción si existe en el diccionario.
Al usar CDN en lugar del CLI, para definir un componente, usamos “Vue.component”, cuyo primer parámetro es el nombre del componente.
Creamos la página html, que incluye la librería de vue, el css y el contenedor de la aplicación, donde incluimos la etiqueta del componente a crear, “<my-translator />” [1].
Además, creamos el fichero “firstvue.css”, con un estilo para agrandar los elementos de tipo texto [2].
Definimos el componente, en el fichero “firstvue.js” [3]. Nombramos al componente como “my-translator”.
let Translator = Vue.component(‘my-translator’, { })
En la propiedad “template”, definimos su plantilla, mediante “template literal”, comillas “`” usadas a principio y fin del html de la plantilla, para visualizarla en varias líneas. Definimos una caja de texto, que representa la palabra “word”, que el usuario escribe para buscar su traducción. Mediante “v-model” vinculamos el input con el modelo.
Incluimos un botón para limpiar el valor del input al pulsarlo, así como las sugerencias que la propiedad computada “AnyMatch”, nos retorna.
En la propiedad “data” incluimos las variables, “placeholderWord”, texto en el placeholder del input, “word” y “dictionary”, que contiene una lista de palabras con sus traducciones entre castellano “ES” e inglés “EN”.
En la propiedad “computed” añadimos la propiedad computada “AnyMatch” que recorre el diccionario y compara cada palabra, de modo que si el valor contiene la palabra “word”, la añade a un array retornado por la función.
En la propiedad “methods” definimos el método “clear”, para vaciar el input al pulsar el botón “limpiar”.
Tras definir el componente, lo añadimos a la instancia de la aplicación, en la propiedad “components”, mediante kebab-case.
const app = new Vue({ el: ‘#app’, components: { ‘my-translator’: Translator } });
Abrimos en el navegador el html, visualizamos el componente, tras introducir letras en la caja de texto, deberían aparecer las sugerencias de traducción [4]. Si pulsamos el botón limpiar, se vacía el contenido del input [5] y las sugerencias.
IMPORTANTE
Los componentes en entorno CDN se declaran como Vue.component, sin embargo su definición varía en entornos CLI, que veremos más adelante.
Los componentes pueden reutilizarse múltiples veces y parametrizarse para condicionar su comportamiento. Realicemos unas modificaciones en el ejercicio anterior para crear dos instancias del mismo componente y que su comportamiento sea diferente en base a un parámetro recibido en las props.
Para ello, el html incluye dos componentes del mismo tipo “my-translator” [1], el primero para traducciones de castellano, indicado mediante la propiedad “prop-language” con el valor “ES”, y el segundo para traducciones de inglés, con valor “EN”. La hoja de estilos “firstvue.css” será idéntica a la anterior [2].
La definición del componente [3] es idéntica al anterior, con algunas variaciones. En la sección de la plantilla, comprobamos el valor de la propiedad “propLanguage”, proporcionada en las props al componente, de modo que, dependiendo del idioma, mostraremos un texto u otro como título, mediante un condicional “v-if”. Este mismo condicional entrará en juego a la hora de mostrar las sugerencias recorridas con el “v-for”.
La sección del data no varía. Sin embargo, la sección de las propiedades computadas, en lugar de establecer por defecto el idioma “ES” lo hacemos dinámico mediante la prop.
this.dictionary.map( (w)=> {if(this.word != ‘’ &&
w[this.propLanguage].toLowerCase().includes(this.word.toLowerCase()) ) words.push(w) })
Tanto tiempo hablando de “propLanguage” y no la habíamos definido; podemos ver cómo una propiedad puede influir en el comportamiento de nuestro componente. Definamos esta propiedad, estableciendo su tipo a texto mediante “type” y su valor por defecto “ES”, es decir, en caso de no indicar la propiedad, tomará este valor.
,props:{ propLanguage: {type:String,default:’ES’} }
Si abrimos el html en el navegador aparecen dos componentes, su título condicionado por el valor del idioma proporcionado a cada componente. Si escribimos en el componente de castellano, sugerirá palabras de castellano a inglés [4], si lo hacemos en el componente de inglés será al revés. Finalmente, una vez queramos borrar el contenido de sugerencias y la palabra, pulsaremos el botón “limpiar” del componente que deseemos vaciar [5], observando un comportamiento aislado entre componentes.
IMPORTANTE
Cuando enviamos propiedades al componente, si ponemos los dos puntos delante del nombre de la propiedad, estamos enviando un valor dinámico, en caso contrario será un valor en plano.
Hemos visto la importancia de las props en los componentes, son la primera línea de entrada de estos y por tanto deben ser el primer punto por validar para que nuestros componentes tengan el funcionamiento esperado.
Creemos un ejercicio para trabajar con los tipos de valores que podemos validar [1].
Para ello, creamos en el fichero “firstvue.js”, el componente “my-props” [2], donde en la sección props, definiremos:
• propRequired: texto, debido al “type:String” y será requerido recibir valor “required:true”.
• propArray: array, su valor por defecto se asigna mediante “default”, en este caso un array vacío. Usamos “validator”, función de validación, para verificar su tamaño mínimo.
• propObject: objeto, cuyo valor por defecto es un objeto vacío. En este caso, queremos tratar el objeto para retornar un valor, lo haremos mediante la propiedad computada “CompleteName”.
• propMultipleValue: permite número, texto o fecha, al incluirlos en un array de tipos permitidos.
Las propiedades recibidas en las “props” podemos utilizarlas directamente en la plantilla, inicializar variables del data o tratar el valor recibido por las props, en las propiedades computadas y retornar el valor tratado, como es el caso de “CompleteName”. Aquí tratamos el objeto recibido, retornando el nombre completo como valor. En caso de no querer tratar el valor recibido, simplemente asignamos el valor de la prop a su respectiva variable en el data.
En la plantilla mostramos los valores de estas variables del data, así como de la propiedad computada.
Finalmente, en “firstvue.html” instanciamos el componente con cada propiedad rellena como debería [3]. De modo que, si visualizamos la página en el navegador, visualizamos los valores correctamente [4].
Sin embargo, si no enviásemos la propiedad requerida, o no enviásemos elementos en el array, la consola del navegador mostraría los errores [5], sería lo esperado, al no superar la validación:
<my-props :prop-array=[] :prop-object=’{“Name”:”Ramón”, “LastName”:”Serrano Valero”, “Age”:34}’
prop-multiple-value=”25-10-1984” />
IMPORTANTE
Las propiedades recibidas por los componentes se reciben al cargar el componente, cualquier modificación de valor posterior, requiere capturar estos cambios mediante watch.
Hasta ahora, enviábamos un valor a un componente, recibiéndolo por las props, sin embargo, no hemos probado que ocurriría si actualizásemos dicho valor. ¿Refrescaría su valor el componente?
Creemos un ejercicio en el que actualicemos el valor de la prop y esta muestre el nuevo valor recibido.
Definimos en “firstvue.html” [1], dentro de la instancia de la aplicación, una caja de texto cuyo valor realice “data-bind” con la variable “valueData” del data de la aplicación cada vez que modifiquemos el texto de este input. Además, instanciamos el componente “my-props-component” que crearemos, al que proporcionamos por su propiedad “prop-value”, el valor de esta variable del data de la aplicación.
En el fichero “firstvue.js”, creamos el componente “my-props-component” [2], en el que definimos una propiedad de tipo texto “propValue”, en las props. Esta inicializa el valor de la variable local “myValue”, del data del componente.
Finalmente, agregamos el componente a la aplicación, mediante “components” y agregamos la variable “valueData” al data de la aplicación, con el valor por defecto “Valor Inicial”.
Abrimos la página html en el navegador, veremos el valor “Valor Inicial” tanto en el input como en el componente [3], dado que es el valor con el que inicializamos dicha variable de la aplicación y es recibida correctamente por las props del componente. Si ahora intentamos cambiar el valor de dicho input, vemos que el valor se está pasando por las props al componente, pero el componente no representa dichos cambios [4].
Esto se debe a que el valor proporcionado en las props inicializa las variables del data del componente la primera vez. De modo que, al recibir actualizaciones, el valor de dicha propiedad no lo controlamos en el componente.
Para controlar estos cambios de valor debemos hacer uso de las “watch”, y en este caso observar cambios en la propiedad “propValue”, por lo que si añadimos este “watch” al componente [5], asignamos el nuevo valor recibido al data. Abrimos de nuevo la página en el navegador y al cambiar el valor, el componente actualiza la vista [6].
IMPORTANTE
Cualquier borrado o añadido de un atributo de tipo objeto del data requiere del uso de “Vue.set” y de “Vue.delete” para activar la reactividad al añadir o borrar atributos respectivamente.
La reactividad en Vue es un concepto muy importante: cualquier cambio en una variable del data representada en la vista será detectada por un observador y la vista se refrescará con los cambios [1].
Aunque, como veremos, no siempre ocurre la reactividad. Creemos un ejercicio que nos permita activar reactividad.
Creamos “firstvue.html”, con la etiqueta del componente “my-reactivity” [2], al que le pasamos un objeto persona.
Este valor por defecto es enviado por la propiedad “propPerson” de tipo objeto al componente, por lo que creemos en el fichero “firstvue.js” [3], el componente.
En la plantilla visualizamos las propiedades del objeto persona, únicamente hemos enviado “FirstName” y “LastName”. Declaramos en el data, una propiedad para el input de edad y otra para el del nombre, así como dos botones, uno para borrar la propiedad “FirstName” y otro para añadir la propiedad “Age”. Si nos fijamos en los métodos definidos, cada vez que cambie el valor del input nombre, el método “OnChangeName” se ejecuta, asignando el valor del “name” al atributo “FirstName” del objeto “person”.
Como el atributo “FirstName” existe en el objeto persona, en la pantalla [4] al cambiar el valor al nombre se observa el cambio, de modo que la vista es actualizada mostrando el valor en pantalla [5].
Si cambiamos el valor en el input de edad, la reactividad es instantánea, al cambiar el valor de una variable del data directamente y no al atributo de un objeto con el que la reactividad tiene problemas [6].
Ahora bien, si borramos la propiedad “FirstName” del objeto “person”, la reactividad no sucede debido a que esta no se activa al añadir o borrar atributos a un objeto [7]. Sucediendo lo mismo al añadir el atributo “Age”, tras pulsar el botón “Añade Edad” [8].
Para forzar la reactividad ante añadir nuevos atributos o asignar valores sobre el atributo de un objeto utilizamos:
Vue.set(objeto,”NombreAtributo”,valor)
Mientras que para activarla en el borrado de un atributo de un objeto usaremos:
Vue.delete(objeto,”NombreAtributo”)
Si modificamos los métodos anteriores por [9] debería ser reactivo todo cambio sobre el objeto, tanto si borramos atributo ||!, como si añadimos atributo [”@, como si asignamos valor [#].
IMPORTANTE
Cuando queramos forzar la reactividad, podemos hacer uso de Vue.nextTick, dado que lo que incluyamos dentro de dicho bloque, se ejecutará tras haber finalizado la asignación en el DOM.
La reactividad es muy caprichosa, la vista no se refresca a no ser que la variable del data observada cambie de valor, sin embargo nos preguntamos ¿qué ocurriría en caso de querer forzar la reactividad asignando un mismo valor?
Veremos cómo la reactividad ocurre la primera vez que cambia el valor de la variable, pero no en sucesivos y cómo forzar esta reactividad.
Creamos el fichero “firstVue.html” [1], donde incluimos la etiqueta del componente “my-reactivity”.
Tras esto, creamos el fichero “firstVue.js”, donde crearemos el componente [2], con un input sobre el que queremos realizar el “focus” tras pulsar el botón. Incluimos la variable “focus”, inicializada a “false”, así como un método “DoFocus”, donde establecemos el valor de la variable a “true”.
Declaramos un watch, de tal forma que cuando ocurran cambios sobre esta variable realice el focus sobre el input, en caso de tener valor “true”.
Abrimos la página html [3], al pulsar el botón, como el valor de la variable era falso, entra en el método y establece el valor a cierto. El “watch” detecta el cambio y realiza el foco [4]. Si lo intentamos de nuevo, no vuelve a realizar el foco porque el valor era cierto y al vuelve a serlo, por lo que el watch no detecta cambios y la reactividad no ocurre [5].
Modificamos el código del método [6], en lugar de asignar siempre a cierto, lo inicializamos a falso, dejamos que dicho valor forme parte del DOM y tras esto, se ejecuta el bloque “Vue.nextTick”, donde cambiamos el valor a cierto.
Abrimos de nuevo el html, el valor es falso, al pulsar, ponemos su valor a falso y tras asignarlo al DOM, se ejecuta el bloque “nextTick” cambiando su valor a true, por lo que el watch detecta el cambio y ocurre la reactividad [7].
Ahora el valor es cierto, pulsamos de nuevo, cambia el valor a falso, lo asigna al DOM y tras esto en el “nextTick”, cambiamos a cierto, detectamos el cambio y ocurre la reactividad [8].
IMPORTANTE
Utilice condicional cuando el contenido a mostrar u ocultar sea poco frecuente, ya que invierte recursos en representar de nuevo el contenido, y afecta al rendimiento.
En ejercicios anteriores hablamos de los condicionales: cuando no cumplen la condición, el contenido en lugar de ocultarse en el DOM, es eliminado. Sin embargo, la directiva “v-show” no elimina del DOM el contenido al no cumplir la condición, sino que la oculta, utilizando el atributo “display” de CSS.
Veamos la diferencia entre ambos, definimos en “firstvue.js”, la variable “step” en el data de la instancia vue:
const app = new Vue({ el: ‘#app’, data(){ return { step:0 } } });
Declaremos condicionales en “firstvue.html” [1], para mostrar un párrafo, dependiendo del valor del “step”, así como también varios párrafos con “v-show”. Un botón incrementa el valor de “step”, para mostrar u ocultar párrafos.
Si abrimos la página html e inspeccionamos los elementos [2], dado que el valor de “step” es 0, se muestra en los condicionales el párrafo correspondiente, pero si analizamos los elementos en la consola del navegador, los condicionales únicamente mantienen en DOM el contenido visible en pantalla, cosa que no ocurre con “v-show”, donde ambos elementos permanecen en DOM, aunque solo uno sea visible en pantalla.
Si pulsamos el botón, incrementamos el valor del “step”, sucede lo mismo, que el condicional en el DOM solo aparece el visible, mientras que en v-show siempre permanecen [3].
Es recomendable utilizar esta directiva “v-show” en caso de que un contenido se muestre y oculte con relativa frecuencia ya que, al permanecer oculto en el DOM, su coste de representarlo es mínimo y tiene mayor rendimiento.
Al refrescar la página, es posible que veamos etiquetas {{step}}, sin valor [4], para evitar esto haremos uso de la directiva “v-cloak”, definiendo unos estilos en la hoja de estilos “firstvue.css” [5] y añadiendo esta directiva en el div de la app [6]. Si ahora refrescamos la página, en lugar de visualizar las etiquetas veremos el texto “Cargando” 7].
IMPORTANTE
El atributo “:key” o “v-bind:key” debe ser una clave única para identificar un elemento en la lista y actualizar solo los elementos modificados, en lugar de todos.
Si el orden de una lista varía al cambiar el contenido de un elemento, Vue copia el contenido a la nueva posición del elemento, permaneciendo a nivel de DOM. Esto es un error, dado que debería mover el contenido a nivel de DOM en lugar de copiarlo. Para ello se necesita asignar una clave única mediante “:key” a cada elemento para identificarlo.
Creamos en el fichero “firstvue.html” un bucle para representar las tareas, añadiendo un “checkbox” en aquellas con estado “done=false”, así como una caja de texto para introducir nuevas tareas mediante un botón [1].
En “firstvue.js” [2], incluimos en el data una variable para hacer el “bind” del texto del input y otra para la lista de tareas. Hemos añadido una propiedad computada que ordena los elementos de la lista, primero por orden alfabético y posteriormente por estado, no obstante, en este primer ejemplo no haremos uso de esta propiedad. Finalmente añadimos el método “Done” para actualizar el estado de la tarea marcada como finalizada mediante el “checkbox”.
Si abrimos la página html en el navegador, aparece la lista sin ordenar, al añadir un elemento aparece en la lista [3]. Si actualizamos el valor del elemento marcando el checkbox, cambiará su estado en la lista [4].
Hasta aquí no hay problemas, ni siquiera requerimos la clave en el bucle, dado que internamente vue tiene mapeada la lista igual que en la vista, sería equivalente a haber añadido la clave “:key=index” a nivel del bucle “v-for”.
Ahora bien, si queremos ver la lista ordenada, lo haremos mediante la propiedad computada “tasksOrdered” en lugar de “tasks”:
<div v-for=”(task,index) in tasksOrdered”>
Al abrir la página, los elementos aparecen ordenados [5] y añadimos dos elementos nuevos [6]. Si actualizamos el estado de uno de estos a realizado se añade en la lista como marcado, pero en la posición que ocupaba sigue marcado el checkbox, aunque de un elemento erróneo. Se debe a no haber indicado un identificador único a la clave.
Si ahora añadimos la clave única de cada elemento “id”:
<div v-for=”(task,index) in tasksOrdered” :key=”task.id”>
Al abrir la página, observamos como al añadir elementos, cuando cambiemos su estado, se representarán correctamente [7].
Pulsuz fraqment bitdi.