Created
June 11, 2022 23:12
-
-
Save JavoEscobar/440d998ac58c94b4adca0bc6bf601ce8 to your computer and use it in GitHub Desktop.
Pokedex sencillo en VueJS para aprender a usar directivas básicas del framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<!-- CASILLA DE BUSQUEDA --> | |
<!-- La directiva v-model de VueJS permite enlazar el valor de un | |
elemento HTML a la variable asociada --> | |
<input v-model="pokemonName" type="text" placeholder="Buscar Pokemon..."/> | |
<!-- LISTADO DE POKEMONS --> | |
<ul> | |
<!-- La directiva v-for de VueJS permite renderizar tantas veces | |
los elementos HTML asociados como elementos tenga el arreglo vinculado --> | |
<li v-for="pokemon in pokemons.filter((el) => { | |
// Filtramos los pokemons. Si aún no se escribe nada en la casilla de | |
// búsqueda, se devuelven todos. Pero si hay algo escrito, nos quedamos | |
// con aquellos que coincidan con la búsqueda | |
return (pokemonName == '' ? true : (el.name.indexOf(pokemonName) >= 0 ? true : false)) | |
})" | |
:key="pokemon.name"> | |
<!-- En el JSON que obtenemos de consultar el API del Pokedex, | |
los nombres de pokemon compuestos vienen separados con guión, | |
los reemplazamos por espacio para facilitar su visualización --> | |
<span @click="showPokemon(pokemon.url)">{{pokemon.name.replaceAll("-", " ")}}</span> | |
</li> | |
</ul> | |
<!-- POPUP CON DETALLES DEL POKEMON --> | |
<div id="popup" class="modal"> | |
<div class="modal-content"> | |
<span class="close" @click="closePokemon">×</span> | |
<div class="row"> | |
<div class="column"> | |
<!-- En VueJS es importante validar que elementos no se conviertan | |
en null o undefined al momento de renderizar por lo que validamos | |
primero que exista un pokemon seleccionado, y tenga imagen. | |
Si no lo tiene, devolvemos una URL vacía para evitar error de Javascript. | |
Para asignar dinámicamente un valor a un atributo de un elemento HTML, | |
como lo es la url de la imagen, empleamos el signo : para devolver | |
el valor de la variable, o propiedad del objeto, relacionado. En este caso | |
la URL del pokemon seleccionado se encuentra en selectedPokemon.sprites.front_default --> | |
<img :src="selectedPokemon.sprites ? selectedPokemon.sprites.front_default : ''"/> | |
</div> | |
</div> | |
</div> | |
</div> | |
</template> | |
<script> | |
import { reactive, ref } from "vue"; | |
export default { | |
// VueJS 3 introduce lo que denomina Composition API. Es una forma mas estructurada | |
// de organizar propiedades y métodos dentro de una función Setup(). | |
// En Vue 3 asignamos ref y reactive al momento de declarar variables, | |
// objetos o arreglos para hacerlos reactivos. Un elemento reactivo es | |
// aquel que le indica a Vue que debe volver a renderizar la interfaz | |
// cuando el elemento es modificado. | |
setup() { | |
const pokemons = reactive([]); | |
const selectedPokemon = reactive({}); | |
const pokemonName = ref(''); | |
const fetchPokemons = () => { | |
fetch("https://pokeapi.co/api/v2/pokemon/?limit=1126") | |
.then((response) => response.json()) | |
.then((data) => Object.assign(pokemons, data.results)); | |
}; | |
const showPokemon = (url) => { | |
fetch(url) | |
.then((response) => response.json()) | |
.then((data) => { | |
// Para disparar la reactividad de Vue en objetos y arreglos, | |
// se debe emplear la el método Object.assign | |
Object.assign(selectedPokemon, data); | |
var popup = document.getElementById("popup"); | |
popup.style.display = "block"; | |
}); | |
}; | |
const closePokemon = () => { | |
var popup = document.getElementById("popup"); | |
popup.style.display = "none"; | |
}; | |
return { | |
pokemons, selectedPokemon, pokemonName, | |
fetchPokemons, showPokemon, closePokemon | |
}; | |
}, | |
// Vue 2 empleaba algo denominado Options API, esto no es mas que | |
// un conjunto de hooks o funciones que se encontraban ya listas, | |
// solo de ser llenadas con lógica de negocio, para ser utilizadas, | |
// a diferencia de la Composition API que requiere declarar primero | |
// los hooks a necesitar. El hook mounted() de Vue 2 se dispara | |
// cuando la aplicación es cargada por primera vez. | |
mounted() { | |
// El hook mounted se dispara cuando se carga la aplicación | |
this.fetchPokemons(); | |
}, | |
}; | |
</script> | |
<style> | |
input { | |
width: 100%; | |
box-shadow: none; | |
border: none; | |
outline: none; | |
font: 200 20px/1.5 Helvetica, Verdana, sans-serif; | |
border-bottom: 1px solid #ccc; | |
} | |
ul { | |
list-style-type: none; | |
margin: 0; | |
padding: 0; | |
} | |
li { | |
font: 200 20px/1.5 Helvetica, Verdana, sans-serif; | |
border-bottom: 1px solid #ccc; | |
} | |
li:last-child { | |
border: none; | |
} | |
li span { | |
text-decoration: none; | |
color: #000; | |
display: block; | |
width: 100%; | |
transition: font-size 0.3s ease, background-color 0.3s ease; | |
} | |
li span:hover { | |
cursor: pointer; | |
font-size: 30px; | |
background: #f6f6f6; | |
} | |
.modal { | |
display: none; /* Hidden by default */ | |
position: fixed; /* Stay in place */ | |
z-index: 1; /* Sit on top */ | |
left: 0; | |
top: 0; | |
width: 100%; /* Full width */ | |
height: 100%; /* Full height */ | |
overflow: auto; /* Enable scroll if needed */ | |
background-color: rgb(0, 0, 0); /* Fallback color */ | |
background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */ | |
} | |
/* Modal Content/Box */ | |
.modal-content { | |
background-color: #fefefe; | |
margin: 3% auto; | |
padding: 20px; | |
border: 1px solid #888; | |
width: 80%; /* Could be more or less, depending on screen size */ | |
} | |
/* The Close Button */ | |
.close { | |
color: #aaa; | |
float: right; | |
font-size: 28px; | |
font-weight: bold; | |
} | |
.close:hover, | |
.close:focus { | |
color: black; | |
text-decoration: none; | |
cursor: pointer; | |
} | |
.row { | |
display: flex; | |
flex-direction: row; | |
flex-wrap: wrap; | |
width: 100%; | |
text-align: center; | |
} | |
.column { | |
display: flex; | |
flex-direction: column; | |
flex-basis: 100%; | |
flex: 1; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment