Clase 12 - DOM y API (Cont.)

Ejercicio de Listado de Contactos

Vamos a discutir la implementación de la solución para las funcionalidades de “Listado de Contactos”.

Funcionalidad Básica

Empecemos por la función agregarContacto(), que recibe los parámetros nombre y apellido. La idea es construir un nuevo objeto contacto con esos valores y agregarlo al listado de contactos.

function agregarContacto(nombre, apellido) {
	// construir nuevo objeto de tipo contacto
	// usando los valores `nombre` y `apellido`

	// agregar el nuevo contacto al listado de contactos

	// actualizar la pantalla, usando mostrarListado()
}

Ahora podemos seguir por la función mostrarListado(). Su responsabilidad es la de “mostrar en pantalla” el contenido de la variable contactos.

function mostrarListado() {
	// encontrar elemento UL donde se van a mostrar los contactos
	// limpiar contenido de elemento ul

	// ordenar el listado
	
	// recorrer listado (array) de contactos
	// para cada uno de los contactos del array
		// crear nuevo elemento LI
		// agregar contenido al elemento LI
		// agregar al UL el nuevo elemento LI creado
}

Esta función la deberíamos ejecutar al principio cuando se carga la página y también cada vez que se agrega un contacto nuevo.

Ordenar por nombre o apellido

Para ofrecer al usuario la opción de ordenar por nombre o apellido se pueden usar diferentes elementos visuales. En el ejemplo a continuación usamos un elemento <select> con varias opciones. La idea es que cuando el usuario elige una opción, se actualice el listado

<select class="form-select" id="ordenar" onchange="mostrarListado()">
	<option value="nombre" selected>Nombre</option>
	<option value="apellido">Apellido</option>
</select>

Para obtener el valor seleccionado en un elemento <select>, podemos acceder al campo value.

let ordenElegido = document.querySelector('#ordenar').value;

En la variable ordenElegido tenemos el campo que queremos acceder del objeto contacto. Este valor puede ser 'nombre' o 'apellido'. Para acceder a ese campo, podemos usar la notación de acceso a objetos con corchetes:

let objeto = {
	primerCampo: "primer valor",
	segundoCampo: "otro valor distinto",
}

let nombreCampo = "primerCampo";
let valorCampo = objeto[nombreCampo]; // en valorCampo queda asignado "primer valor"

Para duplicar (clonar) una lista, sin perder la lista original, podemos hacer

let listaNueva = [...listaOriginal];

Sumando estas ideas, podemos pensar el ordenamiento de la siguiente manera

function mostrarListado() {
	//...

	// ordenar el listado
	// tener en cuenta el orden elegido
	let ordenElegido = document.querySelector('#ordenar').value;
	let contactosOrdenados = [...contactos].sort((a,b) => {
		return a[ordenElegido].localeCompare(b[ordenElegido]);
	})
	
	// recorrer listado (array) de contactosOrdenados
	//...
}

Marcar como Favorito

Pensando de una manera similar, podemos resolver el filtrado de favoritos como un paso más a tener en cuenta al momento de mostrar el listado. Agregamos una opción para que el usuario elija que quiere ver:

<select class="form-select" id="mostrar" onchange="mostrarListado()">
	<option value="todos" selected>Todos</option>
	<option value="favoritos">Favoritos</option>
</select>

Y usamos ese valor como un paso más antes de mostrar.

function mostrarListado() {
	//...

	// ordenar el listado
	//...

	// filtrar el listado
	let contactosFiltrados = [...contactosOrdenados].filter(contacto => {
		if (mostrarFiltro == 'todos') {
			// si eligio ver "todos", siempre devuelvo true
			return true; 
		}
		// si sólo muestra favoritod, devuelvo true sólo si es contacto favorito
		return contacto.favorito; 
	})

	// mostrar listado (ordenado y filtrado)
	//...
}

La otra parte de este enunciado es poder marcar como favorito un contacto. Para esto creamos un botón con el comportamiento necesario.

Cada vez que se marca un contacto como favorito, se vuelve a dibujar el listado.

function mostrarListado() {
	//...

	// ordenar el listado
	//...

	// filtrar el listado
	//...

	// mostrar listado (ordenado y filtrado)
	contactosFiltrados.forEach(contacto => {
		//...

		const btnMarcarFavorito = document.createElement('button');
		btnMarcarFavorito.addEventListener('click', () => {
			contacto.favorito = !contacto.favorito;
			mostrarListado();
		})

		//...
	})
}

Ejercicio de PokeAPI

En este punto vamos a discutir la resolución del ejercicio de la PokeAPI.

Funcionalidad básica

Para resolver el problema debemos segmentarlo en dos problemas más pequeños. El primero es obtener los datos básicos (nombre y url) de todos los pokemon, mientras que el segundo es obtener todos los datos del pokemon que hayamos elegido.

function obtenerTodosLosPokemon(){
    // Implementacion
}

function obtenerDatosPokemon(){
    // Implementacion
}

Para poder obtener los datos de la pokeapi debemos hacer un fetch a la url establecida por la misma, que nos devuelva los datos de los primeros 151 pokémon: https://pokeapi.co/api/v2/pokemon?limit=151

let url = "https://pokeapi.co/api/v2/pokemon?limit=151"

function obtenerTodosLosPokemon(){
    fetch(url)
    .then((response) => response.json())  // Se convierte la respuesta de la API a formato json
    .then((data) => {
        console.log(data)
    })
}

// Llamado a la funcion (para que se ejecute)
obtenerTodosLosPokemon()

Esto devolverá en consola la respuesta de la api, que dentro de un atributo llamado results estará ubicado el listado de los pokemon. Entonces ahora sabemos que data.results equivale al listado de los pokemon. Ya con eso podemos iterar por cada elemento dentro del resultado para mostrarlo en pantalla

let url = "https://pokeapi.co/api/v2/pokemon?limit=151"

function obtenerTodosLosPokemon(){
    // Referencia a ID de <ul>, donde ubicaremos a los pokemon
    let listaPokemon = document.getElementById("lista-pokemon")

    fetch(url)
    .then((response) => response.json())
    .then((data) => {
        data.results.forEach(pokemon => {   // Por cada pokemon en 'data.results':
            //Crear elemento li
            let li = document.createElement('li')
            li.classList.add('list-group-item', 'd-flex', 'justify-content-between')
            li.innerText = pokemon.name

            //Agregar elemento a lista
            li.appendChild(btn)
            listaPokemon.appendChild(li)
        });
    })
}

obtenerTodosLosPokemon()

Ya podemos mostrar los pokemon en pantalla. Consignas uno y dos hechas. Ahora nos queda añadir la posibilidad de interactuar con los elementos de la lista y que, al clickear uno, este nos muestre más datos del pokemon al que hace referencia.

Para esto tenemos varias opciones. La mas simple es ayudarnos de un botón y un eventListener que ejecute una función cuando se clickee sobre el botón. Es importante tener en cuenta que, para no tener que hacer una funcion por cada pokemon, podemos hacer una sola que tome como parámetro los datos que ya tenemos (que son el nombre y la url del pokemon)

Podemos hacer uso de la url del pokemon para buscar todos sus datos individuales y poder así construir una tarjetita en algun lugar de la página

let url = "https://pokeapi.co/api/v2/pokemon?limit=151"

function obtenerTodosLosPokemon(){
    fetch(url)
    .then((response) => response.json())  // Se convierte la respuesta de la API a formato json
    .then((data) => {
        // Referencia a ID de <ul>, donde ubicaremos a los pokemon
        let listaPokemon = document.getElementById("lista-pokemon")
        
        data.results.forEach(pokemon => {   // Por cada pokemon en 'data.results':
            //Crear elemento li
            let li = document.createElement('li')
            li.classList.add('list-group-item')
            li.innerText = pokemon.name

            //Crear botón
            let btn = document.createElement('button')
            btn.classList.add('btn', 'btn-primary')
            btn.innerText = "+"
            btn.addEventListener('click', () => {
                // Llama a la funcion nueva, pasando la url del pokemon como parámetro
                obtenerDatosPokemon(pokemon.url)
            })

            //Agregar elemento a lista
            listaPokemon.appendChild(li)
            listaPokemon.appendChild(btn)
        });
    })
}

function obtenerDatosPokemon(pokemonUrl){
    fetch(pokemonUrl)  // Hacemos fetch a la dirección que tiene el pokemon
    .then((response) => response.json())
    .then((data) => {
        console.log(data)
    })
}

// Llamado a la funcion (para que se ejecute)
obtenerTodosLosPokemon()

Ahora que tenemos los datos, la manera de mostrarlos en pantalla es totalmente a gusto de ustedes. Recuerden que para poder mostrarlos deberemos de colocar el texto en algun lado. Para eso referenciamos por id las etiquetas de HTML que querramos que contengan dichos datos.

Como ejemplo, esta podría ser la funcion ‘obtenerDatosPokemon’

function obtenerDatosPokemon(pokemonUrl){
    // Elementos referenciados por ID
    let pkmnNombre = document.getElementById("pkmnNombre")
    let pkmnImg = document.getElementById("pkmnImg")
    let pkmnTipos = document.getElementById("pkmnTipos")

    fetch(pokemonUrl)  // Hacemos fetch a la dirección que tiene el pokemon
    .then((response) => response.json())
    .then((data) => {
        pkmnNombre.innerText = data.name
        pkmnImg.setAttribute("src", data.sprites.front_default)
        pkmnTipos.innerText = data.types[0].type.name
    })
}

Código HTML

<!DOCTYPE html>
<html lang="es-ES">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!--Bootstrap-->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
    <title>Ejercicio PokeAPI</title>
</head>
<body>
    <div class="container row">
        <h1 class="h1">PokeAPI</h1>
        <div class="justify-content-start col-4" >
            <ul id="lista-pokemon" class="list-group list-group-flush overflow-auto" style="max-height: 800px;">
                <!--Aquí se carga la lista de pokemon-->
            </ul>
        </div>
        <div class="col-8">
            <div class="card">
                <!--Aquí se completan los elementos con los datos del pokemon clickeado-->
                <div id="pkmnNombre" class="card-header">Pokemon</div>
                <div class="card-body row">
                    <div class="col">
                        <img id="pkmnImg" src="" alt="PokemonImg">
                    </div>
                    <div class="col">
                        <span id="pkmnTipos">Tipos</span>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
</body>
<script src="script.js"></script>
</html>

Resultado Final

“Demo Ejercicio”