viernes, 21 de agosto de 2020

Todo con Webpack

1- Descargar archivos HTML y CSS
2- Descargar proyecto de Github de Webpack como ZIP y descrompimir.
3- Vamos a ponerle un nombre nuevo "todoLab" y abrimos en Visual
4- Tenemos el cascaron de Webpack, pero ahora tenemos que iniciar Node.
npm install
npm start

1-TODO CLASS

1- Voy a crear en "src" un nuevo directorio llamado "classes"
2- Dentro de ese directorio voy a crear un archivo para mi clase tarea llamado "todo.class.js"
3- Alli voy a crear mi nueva clase, como tengo que importarlo le voy a poner export delante:
Y en el constructor le voy a pasar una tarea.
En el contructor le voy a poner un nombre a esa tarea, un id unico con la fecha y el get que lo convierte en numero, el codigo quedaria asi:

export class Todo{

    constructor ( tarea ){
        this.tarea = tarea;
        this.id = new Date().getTime(); //2423144255243423
        this.completado = false;
        this.creado = new Date();
    }

}

Ahora, en index.js, voy a llamar a ese archivo:

import './styles.css';

import { Todo } from './classes/todo.class';

const tarea = new Todo('Estudiar React!');
console.log(tarea);


Necesito guardar ahora esas tareas en una lista de tareas, podria hacer alli mismo un array vacio e ir guardando, pero necesito hacer mas funciones, como borrar tarea, editar, etc y entonces me voy a crear una clase especial para eso!.

Me voy al directorio "classes" y luego creo el archivo:
todo-list.class.js

export class TodoList{

    constructor(){
        //Array para meter todas las tareas
        this.todos = [];
    }

    //Agregar tarea al array
    nuevoTodotodo ){
        this.todos.pushtodo );
    }

    //Borrar todas las tareas
    eliminarTodoid ){

    }

    //Si estaba completado a no completado y viceversa Toggle
    marcarCompletadoid ){

    }

    //Borrar todas las tareas completadas
    eliminarCompletados(){
        
    }
}

Y en el index.js:
import { Todo } from './classes/todo.class';
import { TodoList } from './classes/todo-list.class';

const todoList = new TodoList();

const tarea = new Todo('Estudiar React!');
console.log(tarea);


Ahora, que pasa si tengo un monton de clases y necesito importarlas todas?,
Me voy a crear en el directorio "classes" un archivo "index.js" donde voy a importar todas las clases.

En "classes" ---> "index.js":
import { Todo } from './todo.class';
import { TodoList } from './todo-list.class';

export{
    Todo,
    TodoList
}

En "src" ---> index.js:

import { TodoTodoList } from './classes';

AGREGAR LA TAREA A LA LISTA DE TAREAS:
En "src" ---> index.js:
import './styles.css';

import { TodoTodoList } from './classes';

const todoList = new TodoList();

const tarea = new Todo('Estudiar React!');

todoList.nuevoTodotarea );

console.log(todoList);


2-CONSTRUIR TAREAS EN HTML

 Tengo que mostrar ahora esas tareas que voy guardando en la lista de tareas, en el HTML.
Entonces, me voy al HTML:
Me voy a componentes.js, que ya lo tenia de antes y voy alli a crear mi lista:
Y alli me voy a crear una funcion para agregar mis tareas a la lista html.
Le voy a poner un export porque esta funcion la voy a exportar.

 export const crearTodoHtml = ( todo ) => {
    const todHtml = `
        <li class="completed" data-id="abc">
            <div class="view">
                <input class="toggle" type="checkbox" checked>
                <label>${ todo.tarea }</label>
                <button class="destroy"></button>
            </div>
            <input class="edit" value="Create a TodoMVC template">
        </li>`;
}

Ahora, si me fijo en el html, tengo que meter esto dentro de un "ul" con la clase "todo-list", entonces tengo que hacer REFERENCIA a ese elemento.
const divTodoList = document.querySelector('.todo-list');

Ahora, me tengo que crear un elemento tambien para mi "li", porque esto es un STRING.
Y me voy a crear un "div" porque debo meter dentro todo el string del "Li".
const divTodoList = document.querySelector('.todo-list');


export const crearTodoHtml = ( todo ) => {
    const todoHtml = `
        <li class="completed" data-id="abc">
            <div class="view">
                <input class="toggle" type="checkbox" checked>
                <label>${ todo.tarea }</label>
                <button class="destroy"></button>
            </div>
            <input class="edit" value="Create a TodoMVC template">
        </li>`;
    
    const div = document.createElement('div');
    div.innerHTML = todoHtml;
    divTodoList.append(div);

    return div;
}


Ahora, en mi archivo index.js voy a importarlo y voy a meter una tarea en esa funcion.
"Index.js":
-Importo la funcion:
import { crearTodoHtml } from './js/componentes';

-En la funcoin meto una tarea:
crearTodoHtmltarea );

Completo:
import './styles.css';

import { TodoTodoList } from './classes';
import { crearTodoHtml } from './js/componentes';

const todoList = new TodoList();

const tarea = new Todo('Estudiar React!');
const tarea2 = new Todo('Vender en HolaLuz!');

todoList.nuevoTodotarea );
todoList.nuevoTodotarea2 );

console.log(todoList);
crearTodoHtmltarea );


Si recargo, ya se ve la tarea en el browser.
Ahora, vamos a jugar con las class que tiene el CSS de completed y con el check.
Tengo en mi "li" una clase que se llama "completed", si la saco la tarea no aparece tachada, esta clase me permite controlar eso.
Y si yo quito la palabra "check" tampoco va a aparecer el check, tenemos que jugar con esas dos clases.

OPERADOR TERNARIO
Lo vamos a usar mediante la interpolacion de string.
<li class="${ ( todo.completado ) ? 'completed' : '' }" data-id="abc">

Lo mismo con "checked"
<input class="toggle" type="checkbox" ${ ( todo.completado ) ? 'checked' : '' }>

DATA-ID
Si me fijo tengo un atributo en el elemento, en el "li", que es "data-id", alli quiero yo almacenar el "id" del elemento, para poder despues borrarlo, etc.
<li class="${ ( todo.completado ) ? 'completed' : '' }" data-id="${ todo.id }">

SACRA ESE DIV, INSERTAR SOLO PRIMER HIJO.
Tengo que sacar ese div que esta dentro del "ul", no es buena practica, lo usamos solo para poder meter el "li", entonces, en lugar de insertar el "div" vamos a insertar su "Primer hijo", que seria el "li".
..........
    divTodoList.append(div.firstElementChild);


    return div.firstElementChild;
}



3-EVENTO PARA AGREGAR UN TODO

Quiero que si escribo algo y PRESIONO ENTER aparezca como una lista debajo.
Ese input tiene una clase que se llama "new-todo", el input donde se escribe la tarea.
Hago referencia a el en "componentes.js"
const inputTxt    = document.querySelector('.new-todo');

a- Insertar nuevo Todo en Lista

Ahora abajo voy a crear el evento:
inputTxt.addEventListener('keyup', ( event ) => {
    console.log(event)
})

El event me va a decir que tecla esta presionada.
Aca me sirven dos valores:
VALUE (me dice finalmente que escribio)
KEYCODE (que tecla presiono)
El enter tiene el KEYCODE 13.
Primero arriba en el archivo "componenetes.js" voy a importar la clase "Todo" para poner alli mi tarea:
import { Todo } from '../classes';

Y ahora en el evento tengo que evaluar que la tecla sea "Enter" y que no este vacio lo que escriben.

//Eventos
inputTxt.addEventListener('keyup', ( event ) => {
    
    if(event.keyCode === 13 && inputTxt.value.lenght > 0){
        const nuevoTodo = new TodoinputTxt.value );
    }

})

b- Agregar todo al array de todos
Ahora lo que toca es insertar ese todo al arreglo de todos.
Ese arreglo de todoList lo tengo creado en el "index,js" y tengo que exportarlo primero:
Primero en "componenetes.js" lo importo, porque NECESITO MI INSTANCIA de todoList:
import { todoList } from '../index';

Y luego en "Index.js" voy a tener que EXPORTAR ESA INSTANCIA:

export const todoList = new TodoList();

Ahora si:
inputTxt.addEventListener('keyup', (event=> {
   if(event.keyCode == 13 && inputTxt.value.length > 0){
       console.log(inputTxt.value);
       const nuevoTodo = new TodoinputTxt.value );
       todoList.nuevoTodonuevoTodo );
       console.log(todoList);
   }
});

c- Insertar en HTML

Para eso tengo que usar  mi método, y luego borrar valor del input:

//Eventos
inputTxt.addEventListener('keyup', (event=> {
   if(event.keyCode == 13 && inputTxt.value.length > 0){
       console.log(inputTxt.value);
       const nuevoTodo = new TodoinputTxt.value );
       todoList.nuevoTodonuevoTodo );
       console.log(todoList);
       crearTodoHtmlnuevoTodo );
inputTxt.value = '';
   }
});



4-MARCAR COMO COMPLETADO UN TODO

Si yo hago click en el check, se debe poner la propiedad "completado" de mi todo a "true".
Para eso vamos a la clase TodoList y vamos a la funcion "marcarCompletado".
Entonces yo necesito BARRER EL ARRAY, BUSCAR ESE VALOR(ID) Y CAMBIAR ESE VALOR.
//Si estaba completado a no completado y viceversa Toggle
    marcarCompletadoid ){

        console.log(idtodo.id);

        for(const todo of this.todos ){
            iftodo.id == id ){
                
                todo.completado = !todo.completado;
                break;
            }
        }

    }


-Ahora, yo tengo 3 elementos dentro del li : tengo el label, que es el check, el input, el nombre de la tarea y el button  que es el icono para cerrar, yo tengo que hacer un evento cuando haga click en "ul" para ver en que elemento hice click.
Me voy a "componenetes.js":
Y voy a usar divTodoList, (que es mi "ul).
Tengo que ver que evento es:
divTodoList.addEventListener('click', (event=> {
    console.log(event);
});

Pero me trae muchas cosas, quiero ver que elemento html es exactamente, para eso usare "target":
divTodoList.addEventListener('click', (event=> {
    console.log(event.target);
});

Ahora si hago click me muestra o el input, o el button o el label, quiero traer el nombre solo, con:
divTodoList.addEventListener('click', (event=> {
    console.log(event.target.localName);
});

DE ESA MANERA PUEDO IDENTIFICAR EN QUE PARTE DEL "LI" HICE CLICK.
Lo pongo en una constante:
divTodoList.addEventListener('click', (event=> {
    const nombreElemento = event.target.localName;
});

Y tambien tengo que hacer una referencia al "Li" completo para destruir todo el elemento:

divTodoList.addEventListener('click', (event=> {
    const nombreElemento = event.target.localName;
    const todoElemento   = event.target.parentElement.parentElement;
    console.log(todoElemento);
});

Ahora que identifico el "li" completo tengo que extraer ese id que me trae el atributo "data-id".

divTodoList.addEventListener('click', (event=> {
    const nombreElemento = event.target.localName;
    const todoElemento   = event.target.parentElement.parentElement;
    const todoId         = todoElemento.getAttribute('data-id');
    console.log(todoId);
});

Ya tengo todas las variables listas.

-HIZO CLICK EN EL CHECKBOX, O SEA EN EL INPUT?
divTodoList.addEventListener('click', (event=> {
    const nombreElemento = event.target.localName;
    const todoElemento   = event.target.parentElement.parentElement;
    const todoId         = todoElemento.getAttribute('data-id');
    
    if(nombreElemento.includes('input')){
        todoList.marcarCompletadotodoId );
        todoElemento.classList.toggle('completed');
    }
});


5-ELIMINAR UN TODO

Para eliminar un todo vamos a usar FILTER().
Me voy a class listTodo a la funcion "eliminarTodo".

 eliminarTodoid ){

        this.todos = this.todos.filtertodo => todo.id != id );
    }

Ahora me voy otra vez a "componentes.js" al evento de antes y voy a buscar ese button que tiene la "X".

divTodoList.addEventListener('click', (event=> {
    const nombreElemento = event.target.localName;
    const todoElemento   = event.target.parentElement.parentElement;
    const todoId         = todoElemento.getAttribute('data-id');
    
    ifnombreElemento.includes('input') ){
        todoList.marcarCompletadotodoId );
        todoElemento.classList.toggle('completed');
    }else ifnombreElemento.includes('button') ){
        //Eliminarlo del Array
        todoList.eliminarTodotodoId );
        //Eliminarlo del HTML
        divTodoList.removeChildtodoElemento );
    }
});


6-ELIMINAR TODOS COMPLETADOS

Vamos a borrar todos los que esten completados.
Vamos a la clase de nuevo "TodoList" al metodo "eliminarCompletados" y vamos a usar tambien FILTER, le vamos a decir que nos deje en el arreglo todos los que no esten completados.

//Borrar todas las tareas completadas
    eliminarCompletados(){
        this.todos = this.todos.filtertodo => !todo.completado );
    }
Voy a "componentes.js" a crear un evento para cuando haga CLICK en el boton de "borrar completados", si me fijo en mi html ese boton tiene la clase "clear-completed" y tengo que hacer referencia primero a ese elemento.
const btnBorrar   = document.querySelector('.clear-completed');

Y ahora voy a crear el evento:

btnBorrar.addEventListener('click', () => {
    todoList.eliminarCompletados();
});

Ahora los voy a tener que borrar del Html.
Tengo que ELIMINAR DE ABAJO HACIA ARRIBA, porque si elimino desde el ultimo, la posicion indice de los demas sigue siendo la misma.
TENGO QUE RECORRER LOS ELEMENTOS DESDE ATRAS HACIA ADELANTE:

btnBorrar.addEventListener('click', () => {
    todoList.eliminarCompletados();

    //Recorrer los elementos para borrar los completados
    forlet idivTodoList.children.length -1i >= 0i-- ){
        const elemento = divTodoList.children[i];
        console.log(elemento);
    }
});

Como puedo ver en consola, los estoy recorriendo uno a uno a los elementos desde atras.

Ahora VERIFICO SI TIENE LA CLASE COMPLETE Y SI LO TIENE LO ELIMINO:

btnBorrar.addEventListener('click', () => {
    todoList.eliminarCompletados();

    //Recorrer los elementos para borrar los completados
    forlet idivTodoList.children.length -1i >= 0i-- ){
        const elemento = divTodoList.children[i];
        console.log(elemento);
        
        ifelemento.classList.contains('completed') ){
            divTodoList.removeChildelemento );
        }
    }
});




7-LOCALSTORAGE Y SESSIONSTORAGE


Como hacer la informacion persistente aunque yo cierre e inicie el navegador?
Solo sirven para almacenar la info en la web, si vamos a trabajar con Node ya no sirve.
porque son caracteristicas del navegador.

Lo vamos a ver en la pestaña de Application.
En Storage.

LA DIFERENCIA:
Session Storage se expiran los datos cuando cierro el navegador.

No es bueno almacenar datos sensible porque el usuario los ve.

Funcionan asi:

localStorage.setItem('my-key''123456');
sessionStorage.setItem('my-key''1234');

Siempre dentro de llave y valor van SRTING.

Voy a la clase "ListTodo" y voy a hacer dos métodos: uno para guardar en el localStorage y otro para cargar desde el localStorage.
//Guardar en localStorage
    guardarLocalStorage(){

    }

    //Cargar desde localStorage
    cargarLocalStorage(){
        
    }

8-GUARDANDO Y RECUPERANDO TODOS

Me voy a la clase de "todoList" y voy a crear dos metodos:
-Uno para guardar Todos en localStorage
-Otro para traerlos de localStorage y mostarlos.

//Guardar en localStorage
    guardarLocalStorage(){
        
    }

    //Cargar desde localStorage
    cargarLocalStorage(){
        
    }

El primero:
//Guardar en localStorage
    guardarLocalStorage(){
        localStorage.setItem('todo'this.todos);
    }

Ahora, DONDE VOY A LLAMAR A ESTE METODO:
Lo vamos a llamar en varios métodos:

-nuevoTodo()
//Agregar tarea al array
    nuevoTodotodo ){
        this.todos.pushtodo );
        this.guardarLocalStorage();
    }

-eliminarTodo()
Porque cuando eliminemos un todo vamos a volver a guardar los todos que quedan.
 //Borrar todo
    eliminarTodoid ){
        this.todos = this.todos.filtertodo => todo.id != id );
        this.guardarLocalStorage();
    }

-marcarCompletado()
 marcarCompletadoid ){

        for(const todo of this.todos ){
            console.log(idtodo.id);
            iftodo.id == id ){
                
                todo.completado = !todo.completado;
                this.guardarLocalStorage();
                break;
            }
        }

    }

-eliminarCompletados()
//Borrar todas las tareas completadas
    eliminarCompletados(){
        this.todos = this.todos.filtertodo => !todo.completado );
        this.guardarLocalStorage();
    }


AHORA, PARA ALMACENAR EN LOCALSTORAGE TODO TIENE QUE SER UN "STRING" Y YO LE ESTOY MANDANDO UN "ARREGLO"

TENEMOS QUE TRANSFORMARLO EN "JSON"

-Voy a usar el JSON.stringify
Para decirle que me convierta mi arreglo de tareas en un JSON perfecto:
guardarLocalStorage(){
        localStorage.setItem('todo'JSON.stringifythis.todos ));
    }

Si yo meto una tarea nueva y despues refresco el browser, se van a perder , PERO EN EL LOCALSTORAGE SIGUEN, PERSISTEN.

Ahora viene el proceso inverso, necesito leer ese objeto JSON y pasarlo a la lista como arreglo.
Vamos al metodo cargarLocalStorage()

SIEMPRE TENEMOS QUE VERIFICAR QUE ESE OBJETO EXISTE!
//Cargar desde localStorage
    cargarLocalStorage(){
        iflocalStorage.getItem('todo') ){
            console.log(this.todos);
        }else{
            this.todos = [];
        }
    }

Ahora queiro ver que tengo en mi this.todos en mi arreglo, pero este metodo lo TENGO QUE LLAMAR,
DONDE?
LO VOY A PONER EN EL CONSTRUCTOR:

constructor(){
        //Array para meter todas las tareas
        //this.todos = [];
        this.cargarLocalStorage();
    }

Porque igual en el metodo ya estoy cargando un array vacio si no existe nada.

AHORA, si quiero cargar un todo me va a dar un error, porque lo que me trae ese array es un string.
Lo que tengo ahora almacenado es un string.
Tengo que volverlo a su estado original con JSON.parse:
cargarLocalStorage(){
        iflocalStorage.getItem('todo') ){
            
            this.todos = JSON.parselocalStorage.getItem('todo') );
            console.log(this.todos);
        }else{
            this.todos = [];
        }
    }

Vamos a usar el operador ternario para reducir el codigo de este metodo:

cargarLocalStorage(){
        this.todos = (localStorage.getItem('todo'))
                      ? JSON.parselocalStorage.getItem('todo'))
                      : [];
    }


Ahora, me voy a MOSTRAR EN HTML LOS TODOS:
Me voy al "Index.js" y tengo esto:
import './styles.css';

import { TodoTodoList } from './classes';
import { crearTodoHtml } from './js/componentes';

export const todoList = new TodoList();

Le voy a decir:
console.log(todoList.todos);

Para ver lo que trae.
Ahora los tengo que reconstruir en el Html, para eso voy a llamar a ese metodo.

todoList.todos.forEachtodo => crearTodoHtmltodo ) );


9-RECONSTRUYENDO INSTANCIAS DE TODOS

Ahora, un problema: el arreglo de "todos" ahora internamente son OBJETOS.
No son instancias de mi clase ahora, son objetos dentro del array.

Una manera de solucionar eso, me voy a mi class "Todo"

Voy a crear un metodo estatico donde reciba un todo, el objeto de todo , parece un todo pero es un objeto y lo voy a volver a convertir en un array de todos.

Voy a usar desestructuracion de argumentos, le paso directamente las propiedades que quiero.

Creo una nueva instancia de un todo:


static fromJsonobj ){
        const objTemp = new Todoobj.tarea );
    }


export class Todo{

    static fromJson({ idtareacompletadocreado }){
        const tempTodo = new Todotarea );
        tempTodo.id = id;
        tempTodo.completado = completado;
        tempTodo.creado = creado;

return tempTodo;
    }

    constructor ( tarea ){
        this.tarea = tarea;
        this.id = new Date().getTime();
        this.completado = false;
        this.creado = new Date();
    }

}

EN DONDE LLAMO ESTE METODO ESTATICO?

En los metodos de LOCALSTORAGE
 cargarLocalStorage(){
        this.todos = (localStorage.getItem('todo'))
                      ? JSON.parselocalStorage.getItem('todo'))
                      : [];
        
        this.todos = this.todos.mapobj => Todo.fromJsonobj ) );
    }

Tengo que importar "Todo" arriba:
import { Todo } from "./todo.class";

9-APLICAR FILTROS

Tenemos en la web 3 filtros:
-Pendientes
-Todos ( que es el que tiene que venir por defecto)
-Completados

Tenemos que ir a "componentes.js" y tenemos que hacer REFERENCIA  a esos elementos.
A esos botones.

Tengo un "ul" con class "filter" que tiene dentro estos tres botones.
Voy a tomar esa clase "filters".
const ulFilter    = document.querySelector('.filters');

Implementamos un Listener abajo, donde estan los "Listeners".
ulFilter.addEventListener('click', ( event ) => {
    
});

Si yo hago esto: me trae los nombres de esos botones, pero ciudado, porque si hago click al medio de los botones me va a traer "undefined" porque hay otros elementos, espacios, etc. Tengo que tener en cuenta eso:

ulFilter.addEventListener('click', ( event ) => {
    console.log(event.target.text);
});

Lo voy a poner en una constante:
Y le voy a hacer una condicion por lo de "undefined".
ulFilter.addEventListener('click', ( event ) => {
    const filtro = event.target.text;
    if( !filtro ){
        return;
    }
});


Devuelvo un "return" si no trae nada para que se salga de la funcion.
Ahora yo tengo una clase que se llama "hidden" que me hace un display:none.
Yo podria jugar con esa clase para ir filtrando, cuando por ejemplo clicko en "Pnedientes" que me borre todos los que no sean pendientes y asi.

Ahora, me voy a traer todos los elementos de mi lista:

ulFilter.addEventListener('click', ( event ) => {
    const filtro = event.target.text;
    if( !filtro ){
        return;
    }
    forconst elemento of divTodoList.children ){
        console.log(elemento);
    }
});

Pimero, cada vez que yo haga click sobre algun elemento, tengo que remover la clase hidden:

forconst elemento of divTodoList.children ){
        //console.log(elemento);
        elemento.classList.remove('hidden');
    }


ulFilter.addEventListener('click', ( event ) => {
    const filtro = event.target.text;
    if( !filtro ){
        return;
    }
    forconst elemento of divTodoList.children ){
        //console.log(elemento);
        elemento.classList.remove('hidden');
        const completado = elemento.classList.contains('completed');
        switchfiltro ){

            case 'Pendientes':
                ifcompletado ){
                    elemento.classList.add('hidden');
                }
            break;
            case 'Completados':
                if( !completado ){
                    elemento.classList.add('hidden');
                }
            break;
        }
    }
});

Ahora me queda cambiar el cuadrito que esta alrededor del boton, que tiene una clase que es "selected".

Primero, si veo en el HTML tengo anchors, que son esos botones, con la clase "filtro", los voy a llamar a todos, para barrerlos y borrar esa clase "selected".

const anchorFilters = document.querySelectorAll('.filtro');


Bajo a mi listener:
anchorFilters.forEachelem => elem.classList.remove('selected') );

Y ahora le pongo la clase "selected" solo a ese filtro:
event.target.classList.add('selected');

Completo:
ulFilter.addEventListener('click', ( event ) => {
    const filtro = event.target.text;
    if( !filtro ){
        return;
    }
    anchorFilters.forEachelem => elem.classList.remove('selected') );
    event.target.classList.add('selected');

    forconst elemento of divTodoList.children ){
        //console.log(elemento);
        elemento.classList.remove('hidden');
        const completado = elemento.classList.contains('completed');
        switchfiltro ){

            case 'Pendientes':
                ifcompletado ){
                    elemento.classList.add('hidden');
                }
            break;
            case 'Completados':
                if( !completado ){
                    elemento.classList.add('hidden');
                }
            break;
        }
    }
});

No hay comentarios:

Publicar un comentario

Creando plugin Wordpress - 03

  11- USUARIOS -Para crear un usuario lo mas facil desde el admin- FUNCIONES PARA AGREGAR USUARIO: wp_create_user ( string  $username , stri...