Algo interesante que se ve en esta clase es cómo cada vez que se hace algún cambio a la lista es necesario llamar a la función que vacía la tabla y luego la que hace el render de los items. Esto sucede porque se está usando jQuery como librería de renderizado.
Otras librerías (o frameworks) como React, Angular o Vue no requieren el renderizado manual con cada cambio de estado porque utilizan un paradigma de programación llamado reactivo basado en el patrón de diseño de Observers. Entidades de ES que permiten detectar el cambio de “estado” y ejecutar el re-renderizado de los elementos del DOM cuyo valor/contenido dependan de éste. (bueno … más o menos) 😉
Algunas referencias sobre este tema:
Observer (patrón de diseño)
¿Qué es la Programación Reactiva? Una introducción
El Manifiesto de los Sistemas Reactivos
Muy bueno el aporte.
Además de Angular,
¿React y Vue también usan observers?
La función .map de JavaScript puede recibir un segundo argumento que por convención se llama index y nos sirve para obtener la posición de un elemento dentro de nuestro array 👍.
Learn & Understand JavaScript’s Map Function - codeburst
Super aporte, gracias!!
También recibe como tercero el Array al que pertenece el elemento
Practica general del curso sin utilizar jQuery 🤢, para este caso solo se utilizo JS puro lo cual también se conoce como vanilla.
constcompose=(...functions)=>data=> functions.reduceRight((value, func)=>func(value), data);// Se utiliza $ para identificar que una variable o constante hace referencia// a elementos del DOMconst $DESCRIPTION=document.getElementById("description");const $CALORIAS=document.getElementById("calorias");const $CARBOIDRATOS=document.getElementById("carboidratos");const $PROTEINAS=document.getElementById("proteinas");constERROR_CLASS="is-invalid";constSUCCESS_CLASS="is-valid";// Arreglo de elementosletLIST=[];// consiguiendo los atributes para las etiquetas HTMLconstattributesToString=(obj ={})=>{constENTRIES=Object.entries(obj);constATTRS=[];for(let i =0; i <ENTRIES.length; i +=1){constAUX=ENTRIES[i];constATTR=AUX[0];constVALUE=AUX[1];ATTRS.push(`${ATTR}="${VALUE}"`);}returnATTRS.join("");};// Creando etiquetas html con atributosconstcreateTagAttr=obj=>(content ="")=>{const{ tag, attr }= obj;return` <${tag}${attr ?`${attributesToString(attr)}`:""}>
${content} </${tag}>`;};// Generando tag de maner dinamicaconstcreateTag=(tag)=>{constTAG=(typeof tag ==="string")?createTagAttr({ tag }):createTagAttr(tag);returnTAG;};const trashIcon =createTag({tag:"i",attr:{class:"fas fa-trash-alt"}})("");const tableCell =createTag("td");consttableCells=items=> items.map(tableCell).join("");const tableRowTag =createTag("tr");consttableRow=items=>compose(tableRowTag, tableCells)(items);// Asignacion de evntos siempre que se desea asignar un evento aun elemento del DOM// es necesario utilizar addEventListener$DESCRIPTION.addEventListener("keypress",()=> $DESCRIPTION.classList.remove(ERROR_CLASS));$CALORIAS.addEventListener("keypress",()=> $CALORIAS.classList.remove(ERROR_CLASS));$CARBOIDRATOS.addEventListener("keypress",()=> $CARBOIDRATOS.classList.remove(ERROR_CLASS));$PROTEINAS.addEventListener("keypress",()=> $PROTEINAS.classList.remove(ERROR_CLASS));// Limpiando inputsconstcleanInputs=()=>{ $DESCRIPTION.classList.remove(SUCCESS_CLASS); $DESCRIPTION.value=""; $CALORIAS.classList.remove(SUCCESS_CLASS); $CALORIAS.value=""; $CARBOIDRATOS.classList.remove(SUCCESS_CLASS); $CARBOIDRATOS.value=""; $PROTEINAS.classList.remove(SUCCESS_CLASS); $PROTEINAS.value="";};// Actualizando totalesconstupdateTotals=()=>{let carboidratos =0;let proteinas =0;let calorias =0;// Por buenas praxticas se utiliza un forEach y cumple la mism funcion que se busca// practicamente una funcion de alto orden, no utiizo el map por que map siempre regresa// algo y de momento no importa capturar lo que regresa la iteracion que realizamos.LIST.forEach((item)=>{ carboidratos += item.carboidratos; proteinas += item.proteinas; calorias += item.calorias;});document.querySelector("#totalCalorias").textContent= calorias;document.querySelector("#totalCarboidratos").textContent= carboidratos;document.querySelector("#totalProteinas").textContent= proteinas;};// Permite realizar el rendereo de los itemsconstrenderItems=()=>{// otra manera de poder obtener un elemento por el name TAG// solo que este metodo consigue un arreglo de elementosconst $CONTAINER=document.getElementsByTagName("tbody")[0]; $CONTAINER.innerHTML="";constROWS=LIST.map((item, index)=>{const{ calorias, description, carboidratos, proteinas,}= item;const removeButton =createTag({tag:"button",attr:{class:"btn btn-outline-danger",onclick:`removeItem(${index})`,},})(trashIcon);returntableRow([description, calorias, carboidratos, proteinas, removeButton]);}); $CONTAINER.innerHTML=ROWS.join("");};// Agregar elementos en la listaconstaddElement=()=>{const newItem ={description: $DESCRIPTION.value,calorias:parseInt($CALORIAS.value,10),carboidratos:parseInt($CARBOIDRATOS.value,10),proteinas:parseInt($PROTEINAS.value,10),};LIST.push(newItem);cleanInputs();updateTotals();renderItems();};// funcion para validar los inputsconstvalidateInputs=()=>{// Por cuestion de buenas practicas el resultado de una condición// Ternaria tiene que ser asignada a una variable o constanteconstDESCRIPTION_CLASS=(($DESCRIPTION.value)?SUCCESS_CLASS:ERROR_CLASS); $DESCRIPTION.classList.add(DESCRIPTION_CLASS);constCALORIAS_CLASS=(($CALORIAS.value)?SUCCESS_CLASS:ERROR_CLASS); $CALORIAS.classList.add(CALORIAS_CLASS);constCARBOIDRATOS_CLASS=(($CARBOIDRATOS.value)?SUCCESS_CLASS:ERROR_CLASS); $CARBOIDRATOS.classList.add(CARBOIDRATOS_CLASS);constPROTEINAS_CLASS=(($PROTEINAS.value)?SUCCESS_CLASS:ERROR_CLASS); $PROTEINAS.classList.add(PROTEINAS_CLASS);if($DESCRIPTION.value&& $CALORIAS.value&& $CARBOIDRATOS.value&& $PROTEINAS.value){addElement();}};// Permite remover un item seleccionadoconstremoveItem=(position)=>{LIST=LIST.filter((item, index)=> position !== index);updateTotals();renderItems();};
Otra forma de eliminar el item de la lista es utilizando el metodo .filter() quedaria asi:
constremoveItem=(index)=>{//list.splice(index,1) esto sera sustituido por el código debajo list = list.filter((item,i)=> i !== index)updateTotals()renderItems()}
Para saber mas sobre este método : Array.prototype.filter() - MDN 👍🏻
Super! Gracias :)
Viendo este proyecto con JS Vanilla y con JQuery veo que los frameworks como react o vue o angular disminuyen demasiado el trabajo, lo hacen ver sencillo a comparacion de JS Vanilla
La función tag() que creamos me recuerda mucho a la función h() de Vue cuando usamos renderFunctions, solo que digamos que ahí la función es un poco más poderosa por así decirlo.
Y el usar esas funciones con JavaScript para crear elementos se resuelve con JSX xD
En el min 05:02 se observa cómo se cambia la sintaxis de la arrow function de item => { a (item, index) => {. Esto es porque al recibir solo un parámetro, no es necesario encerrarlo entre paréntesis, pero cuando es más de uno sí.
si dice el profesor que bootstrap lo tiene todo la pregunta es, como lo instaló sino tiene ni siquiera un node_modules para que sea alojado... yo observé en la carpeta de css un archivo llamado bootstrap.main.css con un montón de código que me tiene loco de tanto verlo, de donde saco ese archivo es mi pregunta o como lo instalo??
Hola Sofia, ya leí la CDN y es solamente poner una etiqueta link en head y las etiquetas scripts en un archivo HTML, hasta ahí muy bien y lo puedo hacer, pero la pregunta es esta, después de hacer todo lo que me explica la CDN como hago que aparezca este archivo -> bootstrap.main.css???
Simplifiqué el código de tableRow() y la función que genera el botón de borrado (buttonGenerator(idx)) para mayor claridad (par mí):