Guía 09: Promesas, XML y JSON#

Actividades previas#

HTTP: Requerimientos y respuestas#

  1. Visite el sitio web de ReqBin.

  2. Realice las siguientes peticiones HTTP, p.e.:

    1. Petición GET de los productos:

      • URL: https://data-dawm.github.io/datum/reseller/products.json

      • Método: GET

    2. Petición GET de las categorías:

      • URL: https://data-dawm.github.io/datum/reseller/categories.xml

      • Método: GET

  3. Explore la petición HTTP y la respuesta HTTP en la interfaz.

  4. Utilice un cliente de IAG para explicar cómo se estructura la petición y la respuesta de la API; además de los formatos JSON y XML.

Ambiente de desarrollo#

  1. Acceda a su proyecto landing en Codespaces o en su máquina local.

  2. Cree y utilice la(s) rama(s) de desarrollo.

  3. Instale los paquetes y levante el servidor, con:

    npm install
    npm run dev
    

Actividades en clases#

HTML#

  1. En el documento index.html, agregue una sección para mostrar los datos obtenidos de los productos y sus categorías.

    Ver el código
    <section class="bg-slate-50 dark:bg-gray-900"> ... </section>
    
    <section class="bg-slate-50 dark:bg-gray-900">
    
        <div id="container-05" class="px-4 pt-8 mx-auto max-w-md md:max-w-xl">
            <div class="grid grid-cols-1 gap-4 md:grid-cols-1">
                <h2 class="text-4xl font-extrabold tracking-tight text-gray-900 dark:text-white text-center">Productos más
                vendidos</h2>
            </div>
        </div>
    
        <div id="container-06" class="max-w-4xl pt-8 mx-auto">
            <form class="max-w-sm mx-auto">
                <select id="categories"
                class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
                <option selected>Seleccione una categoría</option>
                <option value="1">Category01</option>
                <option value="2">Category02</option>
                <option value="3">Category03</option>
                </select>
            </form>
        </div>
    
        <div id="container-07" class="max-w-4xl pt-5 pb-5 mx-auto">
            <div id="products-container" class="grid grid-cols-1 md:grid-cols-3 gap-3 p-6">
    
                <!-- Products Card -->
                <div class="animate-pulse space-y-4 bg-white dark:bg-gray-800 p-4 rounded-2xl shadow">
                    <div class="w-full h-40 bg-gray-300 dark:bg-gray-700 rounded-lg"></div>
                    <div class="h-6 bg-gray-300 dark:bg-gray-700 rounded w-3/4"></div>
                    <div class="h-5 bg-gray-300 dark:bg-gray-700 rounded w-1/2"></div>
                    <div class="space-y-2">
                        <div class="h-6 bg-gray-300 dark:bg-gray-700 rounded w-full"></div>
                    </div>
                </div>
    
                <!-- Products Card -->
                <div class="animate-pulse space-y-4 bg-white dark:bg-gray-800 p-4 rounded-2xl shadow">
                    <div class="w-full h-40 bg-gray-300 dark:bg-gray-700 rounded-lg"></div>
                    <div class="h-6 bg-gray-300 dark:bg-gray-700 rounded w-3/4"></div>
                    <div class="h-5 bg-gray-300 dark:bg-gray-700 rounded w-1/2"></div>
                    <div class="space-y-2">
                        <div class="h-6 bg-gray-300 dark:bg-gray-700 rounded w-full"></div>
                    </div>
                </div>
    
                <!-- Products Card -->
                <div class="animate-pulse space-y-4 bg-white dark:bg-gray-800 p-4 rounded-2xl shadow">
                    <div class="w-full h-40 bg-gray-300 dark:bg-gray-700 rounded-lg"></div>
                    <div class="h-6 bg-gray-300 dark:bg-gray-700 rounded w-3/4"></div>
                    <div class="h-5 bg-gray-300 dark:bg-gray-700 rounded w-1/2"></div>
                    <div class="space-y-2">
                        <div class="h-6 bg-gray-300 dark:bg-gray-700 rounded w-full"></div>
                    </div>
                </div>
    
            </div>
        </div>
    </section>
    
    <div id="toast-interactive" ... > </div>
    
  2. Compruebe la vista previa del resultado en el navegador.

JS: Fetch con cadena de promesas#

  1. Dentro de la carpeta js, cree el documento javascript functions.js. Declare el modo estricto del documento.

  2. Escribe una función flecha llamada fetchProducts que reciba un parámetro url, con las siguientes instrucciones:

    Nota

    Tome como referencia How to fetch json in JavaScript para realizar una petición http con fetch.

    1. Dentro de la función, utiliza la palabra clave return para devolver el resultado de fetch(url).

    2. Agregue el primer bloque .then(response => { /*  bloque then - 1 */ }). Dentro del bloque then - 1, realice lo siguiente:

      1. Use la estructura if con la condición !response.ok para lanzar un error.

        throw new Error(`Error HTTP: ${response.status}`);
        
      2. Caso contrario, si la respuesta es correcta, retorne response.json() para procesar los datos en el siguiente bloque.

    3. Agregue el segundo bloque .then(data => { /* bloque then - 2 */ }). Dentro del bloque then - 2, retorne un objeto con las claves success (valor true) y body (contenido de data).

    4. Agregue el bloque .catch(error => { /* bloque catch */ }). Dentro del bloque catch, retorne un objeto con las claves success (valor false) y body (mensaje de error error.message).

  3. Exporte la función fetchProducts.

    Ver la solución
    'use strict';
    
    let fetchProducts =  (url) => {
    
        return fetch(url)
            .then(response => {
    
                // Verificar si la respuesta no es exitosa
                if (!response.ok) {
                    throw new Error(`Error HTTP: ${response.status}`);
                }
    
                return response.json();
    
            })
            .then(data => {
    
                // Respuesta exitosa
                return {
                    success: true,
                    body: data
                };
    
            })
            .catch(error => {
    
                // Error en la solicitud
                return {
                    success: false,
                    body: error.message
                };
    
            });
    }
    
    export { fetchProducts }
    

JS: Carga de productos#

Nota

Verifique que el documento js/file01.js sea importado como módulo (type="module") en el documento index.html.

  1. Al inicio del documento js/file01.js, importe la función fetchProducts desde el documento functions.js.

  2. Agregue una función flecha renderProducts en el documento js/file01.js (antes de la función de autoejecución).

  3. Dentro de la función renderProducts, llame a la función fetchProducts con la URL 'https://data-dawm.github.io/datum/reseller/products.json'. Encadena un bloque .then(result => { /* bloque then */ }) para procesar el resultado.

  4. Dentro del bloque then, utilice una estructura condicional para verificar si result.success es true o false.

  5. En caso que es true:

    1. Almacene en container la referencia al elemento con id "products-container" (utilice el elemento document). Elimine cualquier contenido anterior dentro del elemento usando la propiedad innerHTML.

    2. Almacene en products el contenido de result.body. Seleccione solo los primeros 6 productos del arreglo con slice.

    3. Recorra el arreglo products utilizando el método .forEach( product => { /* bloque forEach */ }).

    4. En el bloque forEach:

      1. Cree una tarjeta HTML con la información del producto (imgUrl, title, price, productURL y category_id), utilizando una plantilla de literales (template literals). Almacene el resultado en la variable productHTML.

        Ver el código
        ...
        
        let productHTML = `
           <div class="space-y-4 bg-white dark:bg-gray-800 p-4 rounded-2xl shadow">
               <img
                   class="w-full h-40 bg-gray-300 dark:bg-gray-700 rounded-lg object-cover transition-transform duration-300 hover:scale-[1.03]"
                   src="[PRODUCT.IMGURL]" alt="[PRODUCT.TITLE]">
               <h3
                   class="h-6 text-xl font-semibold tracking-tight text-gray-900 dark:text-white hover:text-black-600 dark:hover:text-white-400">
                   $[PRODUCT.PRICE]
               </h3>
        
               <div class="h-5 rounded w-full">[PRODUCT.TITLE]</div>
                   <div class="space-y-2">
                       <a href="[PRODUCT.PRODUCTURL]" target="_blank" rel="noopener noreferrer"
                       class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 w-full inline-block">
                           Ver en Amazon
                       </a>
                       <div class="hidden"><span class="1">[PRODUCT.CATEGORY_ID]</span></div>
                   </div>
               </div>
           </div>`;
        
      2. Reemplace los marcadores de posición en productHTML con los valores correspondientes del objeto product, utilizando el método replaceAll de las cadenas de texto, p.e.:

        productHTML = productHTML.replaceAll("[PRODUCT.TITLE]", product.title.length > 20 ? product.title.substring(0, 20) + "..." : product.title);
        ...
        productHTML = productHTML.replaceAll('[PRODUCT.CATEGORY_ID]', product.category_id);
        ...
        
      3. Del objeto container, utilice la propiedad innerHTML para concatenar productHTML.

  6. En caso que es false, muestre una alerta con el mensaje de error.

  7. Llame a la función renderProducts en la función de autoejecución.

  8. Compruebe la vista previa del resultado y la consola del navegador para verificar la ejecución del código.

JS: Fetch con async/await#

  1. Modifique el archivo functions.js.

  2. Defina fetchCategories como una función flecha asincrónica que recibe el parámetro url

    let fetchCategories = async (url) => { /* cuerpo de la función */ }
    
  3. Modifique el cuerpo de la función fetchCategories con las siguientes instrucciones:

    Nota

    Tome como referencia Consumiendo una API en JavaScript utilizando Async / Await para realizar una petición http con fetch.

    1. Dentro de la función, defina un bloque try { /* bloque try */ } catch (error) { /* bloque catch */ }.

    2. En el bloque try, almacene en la variable response el resultado esperar la resolución de await fetch(url).

      1. Use la estructura if con la condición !response.ok para lanzar un error.

        throw new Error(`Error HTTP: ${response.status}`);
        
      2. Caso contrario, si la respuesta es correcta:

        1. En la variable text espere por la resolución de await response.text().

        2. En la constante parser cree una nueva instancia de DOMParser().

        3. En la variable data asigne el resultado de convertir el texto a un objeto XML, con el método parser.parseFromString(text, "application/xml").

        4. Retorne un objeto con las claves success (valor true) y body (contenido de data).

    3. En el bloque catch, retorne un objeto con las claves success (valor false) y body (mensaje de error error.message).

  4. Exporte la función fetchCategories.

Ver la solución
'use strict';

let fetchCategories = async (url) => {

    try {
        const response = await fetch(url);

        if (!response.ok) {
            throw new Error(`Error HTTP: ${response.status}`);
        }

        let text = await response.text()

        const parser = new DOMParser();
        const data = parser.parseFromString(text, "application/xml");

        return {
            success: true,
            body: data
        };

    } catch (error) {

        return {
            success: false,
            body: error.message
        };

    }
}

let fetchProducts =  (url) => { ... }

export { fetchCategories, fetchProducts }

JS: Carga de categorías#

Nota

Verifique que el documento js/file01.js sea importado como módulo (type="module") en el documento index.html.

  1. Al inicio del documento js/file01.js, importe la función fetchCategories desde el documento functions.js.

  2. Defina renderCategories como una función asincrónica en el documento js/file01.js (antes de la función de autoejecución).

  3. Dentro de la función renderCategories, defina un bloque try { /* bloque try */ } catch (error) { /* bloque catch */ }.

  4. Dentro del bloque try, almacene en result el resultado de esperar la resolución de await fetchCategories('https://data-dawm.github.io/datum/reseller/categories.xml').

  5. Utilice una estructura condicional para verificar si result.success es true o false.

  6. En caso que es true:

    1. Almacene en container la referencia al elemento con id "categories" (utilice el elemento document).

    2. Reemplace el contenido anterior (la propiedad innerHTML) con la opción predeterminada deshabilitada.

      container.innnerHTML = `<option selected disabled>Seleccione una categoría</option>`;
      
    3. Almacene en categoriesXML el contenido de result.body.

    4. De categoriesXML, utilice el método getElementsByTagName para obtener una colección de elementos category. Almacene el resultado en la variable categories.

    5. Recorra la lista de elementos en categories utilizando el método for (let category of categories) { /* bloque for */ }.

    6. En el bloque for:

      1. En la variable categoryHTML cree una opción HTML con la información de la categoría (id y name).

        let categoryHTML = `<option value="[ID]">[NAME]</option>`;
        
      2. Del elemento category, utilizando los métodos getElementsByTagName y textContent para extraer el valor de id y name.

      3. Reemplace los marcadores de posición en categoryHTML con los valores correspondientes de id y name.

      4. Del objeto container, utilice la propiedad innerHTML para concatenar categoryHTML.

  7. En caso que es false o dentro del bloque catch, muestre una alerta con el mensaje de error.

  8. Llame a la función renderCategories en la función de autoejecución.

  9. Compruebe la vista previa del resultado y la consola del navegador para verificar la ejecución del código.

JSDoc#

  1. Utilice un cliente de IAG en el documento javascript para generar la documentación JSDoc de las funciones creadas. Asegúrese de que los comentarios JSDoc incluyan descripciones, parámetros y tipos de retorno.

  2. Valide su respuesta con JSDoc: La Guía Definitiva para Documentar tu Código JavaScript.

Versionamiento#

  1. Versione local y remotamente la(s) rama(s) de desarrollo en el repositorio landing.

  2. Genere la(s) solicitud(es) de cambios (pull request) para la rama principal y apruebe los cambios.

Vercel#

  1. Verifique el despliegue continuo (CD) del proyecto en Vercel.

Conclusiones#

Actividades autónomas#

Recursos extras#

En redes:

Promesas en JavaScript APIs públicas para probar