No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Proyecto: Eliminar elementos

21/23
Recursos

Aportes 10

Preguntas 1

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Reg铆strate o inicia sesi贸n para participar.

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 鈥渆stado鈥 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:

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 馃憤.

Practica general del curso sin utilizar jQuery 馃あ, para este caso solo se utilizo JS puro lo cual tambi茅n se conoce como vanilla.

const compose = (...functions) => data => functions.reduceRight((value, func) => func(value), data);

// Se utiliza $ para identificar que una variable o constante hace referencia
// a elementos del DOM
const $DESCRIPTION = document.getElementById("description");
const $CALORIAS = document.getElementById("calorias");
const $CARBOIDRATOS = document.getElementById("carboidratos");
const $PROTEINAS = document.getElementById("proteinas");
const ERROR_CLASS = "is-invalid";
const SUCCESS_CLASS = "is-valid";

// Arreglo de elementos
let LIST = [];

// consiguiendo los atributes para las etiquetas HTML
const attributesToString = (obj = {}) => {
    const ENTRIES = Object.entries(obj);
    const ATTRS = [];
    for (let i = 0; i < ENTRIES.length; i += 1) {
        const AUX = ENTRIES[i];
        const ATTR = AUX[0];
        const VALUE = AUX[1];
        ATTRS.push(`${ATTR}="${VALUE}"`);
    }
    return ATTRS.join("");
};

// Creando etiquetas html con atributos
const createTagAttr = obj => (content = "") => {
    const { tag, attr } = obj;
    return `
      <${tag}${attr ? ` ${attributesToString(attr)}` : ""}>
        ${content}
      </${tag}>`;
};

// Generando tag de maner dinamica
const createTag = (tag) => {
    const TAG = (typeof tag === "string")
        ? createTagAttr({ tag })
        : createTagAttr(tag);
    return TAG;
};

const trashIcon = createTag({ tag: "i", attr: { class: "fas fa-trash-alt" } })("");
const tableCell = createTag("td");
const tableCells = items => items.map(tableCell).join("");

const tableRowTag = createTag("tr");
const tableRow = 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 inputs
const cleanInputs = () => {
    $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 totales
const updateTotals = () => {
    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 items
const renderItems = () => {
    // otra manera de poder obtener un elemento por el name TAG
    // solo que este metodo consigue un arreglo de elementos
    const $CONTAINER = document.getElementsByTagName("tbody")[0];
    $CONTAINER.innerHTML = "";
    const ROWS = 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);

        return tableRow([description, calorias, carboidratos, proteinas, removeButton]);
    });
    $CONTAINER.innerHTML = ROWS.join("");
};


// Agregar elementos en la lista
const addElement = () => {
    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 inputs
const validateInputs = () => {
    // Por cuestion de buenas practicas el resultado de una condici贸n
    // Ternaria tiene que ser asignada a una variable o constante
    const DESCRIPTION_CLASS = (($DESCRIPTION.value) ? SUCCESS_CLASS : ERROR_CLASS);
    $DESCRIPTION.classList.add(DESCRIPTION_CLASS);

    const CALORIAS_CLASS = (($CALORIAS.value) ? SUCCESS_CLASS : ERROR_CLASS);
    $CALORIAS.classList.add(CALORIAS_CLASS);

    const CARBOIDRATOS_CLASS = (($CARBOIDRATOS.value) ? SUCCESS_CLASS : ERROR_CLASS);
    $CARBOIDRATOS.classList.add(CARBOIDRATOS_CLASS);

    const PROTEINAS_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 seleccionado
const removeItem = (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:

const removeItem = (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 馃憤馃徎

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

Muy interesante.

const compose = (...functions) => data =>
  functions.reduceRight((value, func) => func(value), data)

const attrsToString = (obj = {}) => Object.keys(obj).map(attr => `${attr}="${obj[attr]}"`).join(' ')

const tagAtrrs = obj => (content = "") => `<${obj.tag}${obj.attrs ? ' ' : ''}${attrsToString(obj.attrs)}>${content}</${obj.tag}>`

const tag = t => typeof t === 'string' ? tagAtrrs({tag: t}) : tagAtrrs(t)

const tableRowTag = tag('tr')
//const tableRow = items => tableRowTag(tableCells(items))
const tableRow = items => compose(tableRowTag, tableCells)(items)

const tableCell = tag('td')
const tableCells = items => items.map(tableCell).join('')

const trashIcon = tag({tag: 'i', attrs: {class: 'fas fa-trash-alt'}})('')

let description = $('#description')
let calories = $('#calories')
let carbs = $('#carbs')
let protein = $('#protein')

let list = []

description.keypress(() => {
  description.removeClass('is-invalid')
})

calories.keypress(() => {
  calories.removeClass('is-invalid')
})

carbs.keypress(() => {
  carbs.removeClass('is-invalid')
})

protein.keypress(() => {
  protein.removeClass('is-invalid')
})

const validateInputs = () => {

  description.val() ? '' : description.addClass('is-invalid')
  calories.val() ? '' : calories.addClass('is-invalid')
  carbs.val() ? '' : carbs.addClass('is-invalid')
  protein.val() ? '' : protein.addClass('is-invalid')

  if(
    description.val() &&
    calories.val() &&
    carbs.val() &&
    protein.val()
  ) add()
}

const add = () => {
  const newItem = {
    description: description.val(),
    calories: parseInt(calories.val()),
    carbs: parseInt(carbs.val()),
    protein: parseInt(protein.val())
  }
  list.push(newItem);
  cleanInputs()
  updateTotals()
  renderItems()
}

const updateTotals = () => {
  let calories = 0, carbs = 0, protein = 0

  list.map( item => {
    calories += item.calories;
    carbs += item.carbs;
    protein += item.protein;
  })

  $('#totalCalories').text(calories)
  $('#totalCarbs').text(carbs)
  $('#totalProtein').text(protein)
}

const cleanInputs = () => {
  description.val('')
  calories.val('')
  carbs.val('')
  protein.val('')
}

const renderItems = () => {
  $('tbody').empty()

  list.map((item, index ) => {

    const removeButton = tag({
      tag: 'button',
      attrs: {
        class: 'btn btn-outline-danger',
        onclick: `removeItem(${index})`
      }
    })(trashIcon)

    $('tbody').append(tableRow([item.description, item.calories, item.carbs, item.protein, removeButton]))
  })
}

const removeItem = (index) => {
  list.splice(index, 1)

  updateTotals()
  renderItems()
}

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 est谩s guardando la lista en el localStorage, recuerda actualizarla al momento de eliminar el elemento de la lista

const removeItem = index => {
  list.splice(index, 1);
  updateTotals();
  renderItems();
  localStorage.setItem('list', JSON.stringify(list))
}

Cuando elimino el item me dice que deleteItem no esta definida pero si lo esta
ademas la funcion me aparece sombreada como si nunca la hubiese llamado

const deleteItem= (index)=>{
list.splice(index,1)

  updateTotals()  
  renderItems()
}



const renderItems = () => {

  const listWrapper = document.querySelector('tbody')

  listWrapper.innerHTML = ""  

  list.map((item,index) => {    
    const boton = tag({
      tag:"button",
      attrs:{
        class:"btn btn-outline-danger",
        onclick: `deleteItem(${index}) ` 
      },
    })(trashIcon)
    
    listWrapper.innerHTML += tableRow([
      item.description, 
      item.calories, 
      item.carbs, 
      item.protein,
      boton   
    ])
  })
}

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