Skip to content

Instantly share code, notes, and snippets.

@JavoEscobar
Created June 11, 2022 23:12
Show Gist options
  • Save JavoEscobar/440d998ac58c94b4adca0bc6bf601ce8 to your computer and use it in GitHub Desktop.
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
<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">&times;</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