Vamos a continuar con el resto de componentes de nuestra página de perfil de jugador.
En esta ocasión, tenemos que mostrar el resto de héroes que posee un jugador, si es que tiene alguno más. Para ello, haremos uso de las tablas de bootstrap-vue, que son muy fáciles de usar y versátiles.
Como vamos a crear otro bloque de componentes, tenemos que crear los directorios correspondientes. Lo que vamos a mostrar es un listado de héroes en una tabla, por lo tanto, suena razonable llamar a la carpeta de nuestro componente HeroesList. Creamos esta carpeta al mismo nivel que la de TopHeroes, es decir, dentro de MainBlock. Dentro de esta, vamos a crear tres componentes: Index.vue (el componente principal, como ya es habitual), HeroIco.vue y HeroClassLevel.vue.
En el componente TopHeroes hemos usado los tres primeros héroes del array de héroes que nos devolvía la API. En este caso vamos a usar el resto de héroes que no se hayan usado en TopHeroes.
Antes de poder usarlo, tenemos que comprobar que el array de héroes tiene más de tres elementos, por lo tanto vamos a crear dos computed que nos ayuden con estas tareas:
// ¿Hay más de tres elementos en el array?
hasHeroesList () {
returnthis.profileData.heroes.length > 3
}
En caso afirmativo, dame todos los elementos del array sin contar los tres primeros. Seguimos con la otra propiedad computada:
Ahora que tenemos inyectados en el hijo los datos de los héroes que faltan por pintar, podemos empezar a trabajar con ellos en el componente HeroesList/Index.vue.
Lo primero que vamos a hacer es definir las props, que se las estamos pasando desde el padre pero el componente hijo no las tiene definidas:
Para pintar la tabla en pantalla lo que tenemos que hacer es usar el componente tabla y pasarle como prop el array de héroes.
Como tenemos el fondo oscuro, el texto negro de la tabla casi no se vería. Le agregamos la propdark:
Nosotros no queremos mostrar todos los elementos del array de héroes, por lo tanto vamos a definir que campos (columnas) queremos mostrar en nuestra tabla de héroes.
Para ello, siguiendo la documentación, creamos un objeto fields con las opciones y las columnas que queremos mostrar de nuestra tabla. Lo hacemos de la siguiente forma:
Ya tenemos las columnas y las opciones que nos interesan listas para ser usadas. Actualicemos nuestra tabla agregando una nueva propfields que recibirá como valor el objeto con los campos que acabamos de definir:
<b-table:items="heroes":fields="fields"dark/>
Ahora nuestra tabla se ve mejor. Aún seguimos teniendo acceso a todos los demás datos, simplemente no los estamos mostrando.
El componente tabla de bootstrap-vue soporta muchas opciones. Vamos a personalizarlo un poco más con esta configuración:
Si seguimos lo que dice la documentación de las tablas de bootstrap-vue, podemos entender qué hace cada propiedad.
Aparte de los items que le hemos pasado y la definición de las columnas que queremos, le estamos dando un tema oscuro, con efecto “on mouse hover” en cada fila, tamaño pequeño, diferenciado por colores, y, por último, una alternativa responsive para cuando la tabla tenga un tamaño pequeño.
OJO: Las propiedades de tipo Boolean de un componente Vue, cuando estas pasando el valor true, las puedes usar de 2 formas:
Es decir, si queremos que la tabla tenga el tema oscuro, según la definición de su propiedad dark, podemos usar <b-table dark/> o <b-table :dark="true"/>.
Estos nos permite tener, por ejemplo, una computed property que controle el estado de la propiedad dark. En ese supuesto caso podríamos alternar entre el tema oscuro y el tema claro de la tabla así: <b-table :dark="tableTheme"/>.
Ahora tenemos que darle formato a las filas de la tabla, por ejemplo, en base a la class que tenga el heroe. Es decir, en base al tipo de personaje que sea, podemos mostrar su ícono o rostro correspondiente. Lo mismo con el resto de valores; si es leyenda, si es hardcore, etc.
Para personalizar el contenido de la tabla vamos a hacer uso de los Scoped Slots de Vue junto con la funcionalidad de las tablas de Bootstrap-vue:
Los Scoped Slots nos brindan un mayor control sobre cómo aparecen los datos de la tabla. Podemos usar Scoped Slots para proporcionar una vista personalizada para un campo (columna) en particular.
Para poder continuar con los slots, necesitamos darle contenido a los componentes que hemos creado anteriormente (HeroIco y HeroClassLevel).
Estaría bien mostrar la imagen correspondiente al héroe, que representa su clase y género. Además del nombre, el nivel y la clase, deberíamos indicar de alguna forma si es de temporada y si es hardcore. Por último, deberíamos mostrar el nº de kills que tiene actualmente ese personaje.
En el componente HeroIco vamos a encargarnos de mostrar: la imagen del héroe, el nombre, si es de temporada o no (🍃) y si es hardcore.
Este HTML es bastante sencillo. Reusamos clases CSS para mostrar el rostro de nuestro héroe a través de una clase según el tipo y el género. Si el hero es hardcore, le ponemos el border de la imagen rojo al igual que el texto del nombre. Si es de temporada, mostramos la ya reconocida hojita verde.
Definimos las propiedades que va a recibir el componente y con una computed generamos la clase CSS que va a tener: según el género, si es hardcore y el tipo (o clase) de héroe.
Recuerda que esto lo tenemos definido en el CSS global, que lo usamos con los Sprites de CSS.
Ya por último, un par de líneas de CSS para personalizar un poco más el diseño de nuestro componente:
En este componente vamos a incorporar algo que no hemos usado hasta el momento, los Mixins.
Los mixins son una forma flexible de crear funcionalidades reutilizables. Un objeto mixin puede contener cualquier opción de componente.
Lo único que estamos haciendo en este mixin es exponer un method. Cuando importemos el mixin en un componente, en este componente tendremos acceso a este método classToName, como si de un método normal se tratara.
Además, estamos haciendo uso de otro archivo (heroClasses), que tenemos que crear ahora mismo dentro de /src/utils, con el nombre de heroClasses.js y que tiene un contenido muy simple:
De esta forma, si necesitamos pintar el tipo de una clase de personaje, podemos usar esto.
Para usar esta funcionalidad, tenemos que importar y dar de alta el mixin de manera local en nuestro componente. Una vez hayamos hecho esto, podremos acceder al método que acabamos de crear (classToName) como si fuese un method interno del componente.
Para hacer esto en nuestro componente HeroClassLevel.vue, en el bloque de javascript, hacemos lo siguiente:
<script>// Traemos el mixinimport heroName from'@/mixins/heroName.js'exportdefault {
name: 'HeroNameLevel',
// Lo damos de alta
mixins: [heroName],
props: {
hero: {
required: true,
type: Object
}
}
}
</script>
Ahora tenemos acceso al método desde cualquier parte del componente, como si fuera un method normal:
En el HTML, classToName(val)
Desde JavaScript, this.classToName(val)
Ahora sí, podemos crear el HTML de nuestro componente HeroClassLevel.vue:
Haciendo uso del mixin, estamos mostrando el tipo de héroe normalizado. Es decir, si el tipo es demon-hunter, lo que vamos a renderizar en la vista es Demon Hunter. Además estamos mostrando el nivel. Es un componente muy básico.
Este componente no necesita clases CSS, por lo que ya estaría completo.
Hemos creado un mixin de Vue que hace uso de otros archivos y lo hemos usado en nuestro componente.
El código completo de lo que hemos hecho hasta el momento se vería así:
Ahora que ya tenemos nuestros componentes, vamos a modificar la tabla que hemos creado anteriormente, y aprovecharemos para darle estilos CSS que mejoren el aspecto de nuestro componente.
Ahora, haciendo uso de los slots de Vue y del componente tabla, vamos a personalizar el contenido de nuestras celdas.
En la primera columna vamos a insertar el componente HeroIco.vue. Lo haríamos de la siguiente forma, dentro de la tabla:
En este fragmento de código estamos indicando que la columna name (nombre del campo que hemos definido en la variable fields) de la tabla muestre el componente HeroIco en vez de el texto por defecto.
A estas alturas, seguramente te hayas dado cuenta de que esto no puede funcionar si no has registrado el componente.
Por lo tanto, siguiendo en la tabla del componente /HeroesList/Index.vue, deberíamos hacer lo siguiente:
A falta de la última columna de la tabla, nuestra app debería verse así:
Lo bueno de hacer componentes es que nuestra app es más modular, y, por lo tanto, fácil de testear.
Sin embargo, podemos hacerlo sin usar un componente. Para ver cómo se haría, en la tercera columna de nuestra tabla no vamos a usar un componente para mostrar los datos, aunque no sea lo recomendado:
La columna kills de la tabla tiene (o puede tener) unos valores numéricos bastante grandes. Parece una buena idea formatear el valor de dicho campo haciendo uso de los puntos (.) y/o de las comas (,) para facilitar su lectura.
Para realizar esta tarea, en vez de desarrollar una función que haga el trabajo, vamos a usar una librería JavaScript llamada Numeral.js. Numeral es, según dicen en la web, una librería para formatear y manipular números. Justo lo que necesitamos.
Vamos a crear un filtro de Vue, es decir, una función JavaScript que cuando le pasemos un número nos lo devuelva formateado.
Para eso, en nuestra carpeta /filters creamos un archivo llamado numeral.js. Los pasos a seguir son 3:
Importar la librería
Crear la función que será nuestro filtro Vue
Exportar la función para que pueda ser usada
// Paso 1import numeral from'numeral'// Paso 2// Función que recibe un argumento (Número o String numérico) y lo devuelve formateado// Si no hay numero, devolvemos 0const formatNumber = (num) => {
if (!num) {
return0
}
return numeral(Number(num)).format()
}
// Paso 3export {
formatNumber
}
Esto que acabamos de hacer de manera tan sencilla, es un filtro. Para usarlo, es lo mismo que con los mixins o con los componentes.
Puedes usarlo a nivel global de la app o usarlo a nivel local en el componente que lo necesites. En este caso lo vamos a usar de manera local.
Para ello, en el componente donde tenemos la tabla que estamos usando, es decir, en /Profile/MainBlock/HeroesList/Index.vue, agregamos lo siguiente:
Lo primero, traer la función que acabamos de crear
import { formatNumber } from'@/filters/numeral'
Lo segundo, dar de alta esta función (formatNumber) en el componente, para que pueda ser usado desde el template. Como se trata de un filtro, lo haremos desde el bloque de filters:
Ahora la tercera columna de nuestra tabla se vería así:
Haciendo pruebas con usuarios de otras regiones, encontré uno de la region de Korea (KR) con personajes hardcore de temporada: 오빠-3239. Este usuario (http://localhost:8080/#/region/kr/profile/오빠-3239) se vería así:
Como puedes ver en esta imagen, los tres Top Heroes tienen los elite kills sin formatear. Ahora que tenemos el filtro creado, ¿por qué no lo usamos para formatear este valor?
Abrimos el fichero /views/Profile/MainBlock/TopHeroes/TopHero.vue, importamos el filtro, lo habilitamos para poder usarlo en el template y lo usamos para formatear:
A partir de ahora, cada vez que tengamos que formatear un valor numérico, podemos usar este filtro que hemos creado. Y si nos hace falta formatear otro valor con otro formato, siempre podemos crear otro.
Este es el código completo de los componentes que hemos usado en este tema:
Como nota aclaratoria, para el próximo Vue 3 el Core Team tomó la decisión de eliminar los filters, por lo que cada filtro personalizado pasaría a ser como una función.
Esto viene a una confusión que existe de entre si utilizar una función o un filtro para dar formato a un texto.
Increíble clase, la alternativa responsive de las tablas de boostrap me gustó, practicamente lo pone en filas, es genial!
En la clase se menciona que no habiamos usado mixins pero si que los habiamos usado para el set error jaja, igual es bueno ver cómo podemos aplicar los mixins.
Algo importante para esta clase, en el código que hay aquí, al mandarle la información a HeroCassLevel, en la etiqueta del componente pasó la palabra: class, pero en realidad debía ser classSlug, así que esto puede dar error y no mostrar los classSlugs, a como debería quedar el componente escrito es:
Algo bueno que puedo rescatar de uno de los enlaces externos es que podemos acortar la sintaxis del v-slot usando su shorthand: “#” por lo que podríamos sobreescribir los v-slots de esta maera:
En mi caso como estoy usando vue3, ya no existen los filtros, ahora se recomienda crear métodos computados, pero también se pueden registrar globalmente para no hacer el import cada vez que lo usemos, dejo el ejemplo:
¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.