Ahora, en el componente MainBlock, en el bloque de la derecha, quitamos el texto que teníamos puesto y ponemos el componente que acabamos de crear. El HTML de /MainBlock/Index.vue se vería así:
<template><divclass="grid-container"><divclass="grid-item item-left"><TopHeroesv-if="hasHeroes":heroes="topHeroes"/><HeroesListv-if="hasHeroesList":heroes="heroesList"/><ProgressList:acts="profileData.progression"/></div><!-- Right Bar--><divclass="grid-item item-right"><PlayerStats:stats="statsData"/></div></div></template>
La prop que le estamos pasando a PlayerStats con el nombre de stats, es una computed property. statsData tiene el siguiente contenido:
Lo que devuelve statsData son los datos que necesita nuestro componente de stats:
Paragon level: nivel de leyenda.
Kills: monstruos y élites.
Time played: Tiempo jugado por héroe en porcentaje.
Esto quiere decir que si has jugado 10 minutos con el monje y 5 minutos con el cruzado vas a tener el monje al 100% y el cruzado al 50%. No está basado en horas o minutos.
El código completo del componente MainBlock (sin contar el CSS, que no ha cambiado) es este:
Por otro lado, el componente TimePlayed.vue itera sobre el objeto que contiene los tiempos por cada personaje, y, en su interior, hace uso del componente de barra de progreso de bootstrap-vue (https://bootstrap-vue.js.org/docs/components/progress).
El bloque <script></script> del componente PlayerStats/Index.vue es el siguiente:
Como has podido ver, este componente no tiene ninguna complicación. Simplemente pinta los datos que le pasamos: el ícono, color del ícono y valor numérico (formateado con el filtro de numeral). Deberías ver algo así:
Acuérdate, a los íconos los estamos pintando gracias a FontAwesome.
TimePlayed.vue tiene una configuración especial que vamos a explicar ahora. Cuando estamos definiendo las props de un componente, podemos validar el valor que recibe la propiedad a través de una función y definir el tipo de variable que espera dicha propiedad. Lo hemos hecho en algún caso. Ejemplo:
¿Qué pasa si yo quiero validar un tipo (type) de objeto específico?
Algo que no sea, por ejemplo, un String o un Object. Vue nos permite crear nuestros propios tipos de dato y poder usarlos como type.
Custom Types
Con un ejemplo lo verás más claro. Imagina que queremos crear un tipo que sea Persona. Una Persona tiene nombre y el apellido. Podríamos crear algo como esto:
Esto es lo que vamos a hacer en nuestro componente TimePlayed: vamos a crearnos un tipo de dato personalizado para poder usarlo en la definición de las props.
Lo primero que vamos a hacer es crear la función tipo. Vamos a crearla en la carpeta /utils. Al fichero, le vamos a dar el nombre de typeValidation.js. El contenido es el siguiente:
/**
* Used for custom validations
* @param hero {String}
* @param time {String}
* @param classSlug {String}
* @constructor
*/functionHeroData (hero, time, classSlug) {
this.hero = hero
this.time = time
this.classSlug = classSlug
}
export {
HeroData
}
Con esto hemos creado una función que usaremos como un nuevo tipo de dato. Podemos usar esta función y definir nuestra propiedad del componente como HeroData 💃.
A continuación, si revisas cómo está usándose el componente TimePlayed en PlayerStats/Index.vue, puedes ver que tiene este HTML:
<TimePlayed:timePlayed="timePlayed"/>
Sin embargo, no tenemos nada definido como timePlayed dentro de nuestro componente PlayerStats.
Para crear este dato, en el componente PlayerStats/Index.vue creamos una computed property con este nombre, de esta forma:
Lo que estamos haciendo en esta computed property es obtener todas las claves (keys) de este objeto, ordenarlas con sort, y, sobre este array de items ordenados, usar la función map. Con esto estaríamos generando un array de objetos con estos datos:
Con esto ya tenemos el nombre del héroe normalizado y el valor del tiempo. Además, estos datos los hemos creado con nuestra función HeroData, que utilizaremos más adelante.
Vayamos entonces al componente PlayerStats/TimePlayed.vue. Este componente también es muy sencillo y no requiere una explicación extensa:
Un v-for para recorrer el array de elementos de tipo HeroData que le hemos pasado como property y para cada elemento renderizamos el componente TimePlayedHero. Es aquí donde vamos a usar nuestra custom validation.
Vamos a explicar el componente TimePlayedHero.vue:
El bloque de JavaScript tiene lo siguiente:
<script>// Traemos nuestro tipo personalizadoimport { HeroData } from'@/utils/typeValidation'exportdefault {
name: 'TimePlayedHero',
props: {
// Definimos la propiedad con el tipo personalizado 'HeroData'
heroTime: {
type: HeroData,
required: true
}
},
computed: {
// Cada héroe tiene un color// Creamos una clase por cada héroe
classHeroBg () {
return`hero-bg-color-${this.heroTime.classSlug}`
}
}
}
</script>
La propiedad heroTime que recibe el componente , y que es de tipo HeroData, tiene este formato:
Lo bueno de haber usado el custom type es que si en vez de pasarle un dato de tipo HeroData le hubiéramos pasado un dato con el mismo formato que HeroData (es decir, un objeto con las 3 claves: hero, time y classSlug) pero que no ha sido creado con el constructor de HeroData, Vue nos indicará que ese tipo no es el esperado.
En este caso, para forzar el error, he usado un objeto en vez del tipo HeroData. Si lo cambias, deberías ver un error como este: Es aquí dónde vemos la verdadera utilidad de los custom types.
Como hemos establecido el valor máximo (:max) de la barra de progreso en 1, no hace falta que transformemos el valor (heroTime.time).
Con la clase CSS resultante de ejecutar classHeroBg, estamos estableciendo un color para cada personaje.
Ahora solo falta el bloque de CSS. En esta ocasión, vamos a ver cómo generar bucles y variables de CSS con Stylus.
// Definimos una variable, parecida a un objeto javascript// clave:valor$heroesBg = {
barbarian: #4e1c16,
crusader: #795548,
demon-hunter: #F44336,
monk: #ff9800,
necromancer: #00bcd4,
witch-doctor: #8bc34a,
wizard: #3f51b5
}
// Creamos las clases CSS necesarias.progress-time-playedh5.titlecolor#000.w-50pposition relative
width50pxbottom -2pxborder-bottom-left-radius0border-bottom-right-radius0// Bucle "for"// Itera sobre la variable $heroesBg// "hero" es la clave// "bgColor" es el valorfor hero, bgColor in$heroesBg
.hero-bg-color-{hero}
background-color bgColor
{hero} se va a sustituir por la clave en $heroesBg, que correspondería a: ‘barbarian’, ‘crusader’, ‘monk’, ‘necromancer’, etc.
bgColor es el valor, es decir, los colores que hemos definido: ‘#4e1c16’, ‘#795548’, etc.
Con este bucle (de tres líneas) estamos creando todas la clases CSS necesarias para cada tipo de personaje.
Genial, como siempre stylus dandome problemas con la identación al copiar y pegar lo estilos… cosa que no pasaría si usaramos llaves xD
Si seguimos el mismo patrón de carpetas del curso, creo que lo más recomendable hubiese sido crear una carpetas dentro de PlayerStats llamada “TimePlayed” y ahí meter el Index.vue (TimePlayed.vue) y el TimePlayedHero.vue:D
Por último, faltó indicar que en el Index.vue de PlayerStats se tenía que importar el HeroData para poder usarlo:
import { HeroData } from'@/utils/typeValidation'
Adicionalmente, a mi VSCode me marcaba una opción de que la forma de usar HeroData como clase estaba deprecada para ES2015, y me dio está alternativa que se me mucho más elegante usando ya las clases de ES2015:
class HeroData {
constructor (hero, time, classSlug) {
this.hero = hero
this.time = time
this.classSlug = classSlug
}
}
¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.