DEV Community

Cover image for Firebase CRUD con JS y HTML: Read, Update y Delete
Javier Rodriguez
Javier Rodriguez

Posted on • Updated on

Firebase CRUD con JS y HTML: Read, Update y Delete

En la segunda parte de este proyecto, terminaremos de implementar las otras funciones (Read, Update y Delete), siguiendo dando estilos con Bootstrap. Al final de este artículo, tendremos una app funcional para guardar, editar o borrar tareas.

1. Leamos datos

🟨 Agreguemos otro cachito de código a nuestro script para que pueda leer datos. Para esto, vamos a necesitar tener algún "container" en nuestro archivo html para poder contener todas las tareas, y debe ser con un id. En mi caso, pongo esto debajo del form:

<div id="task_todo">
</div>
Enter fullscreen mode Exit fullscreen mode

Y luego desde el script, obtengo el elemento e inserto los datos consultados de la DB (pongo todo el script entero, ya que hice un par de cambios de nombres con respecto al anterior artículo):

const db = firebase.firestore();

const todoForm = document.getElementById('todo_form');
const taskToDo = document.getElementById('task_todo');

const createTask = (name, url, description) => {
    db.collection('tasks').doc().set({
        name,
        url,
        description
    });
};

const getTasks = (callback) => db.collection('tasks').onSnapshot(callback);

window.addEventListener('DOMContentLoaded', async (e) => {
    getTasks((querySnapshot) => {
        taskToDo.innerHTML = '';
        querySnapshot.forEach(doc => {
            console.log(doc.data());
            const if_url = `<a href="${doc.data().url}">URL de tarea</a>` 
            taskToDo.innerHTML += `
                <div>
                    <h4>${doc.data().name}</h4>
                    <p>${doc.data().description}</p>
                    ${doc.data().url ? 
                        if_url
                        : ''
                    }
                </div>
            `
        });
    });
});

todoForm.addEventListener('submit', async e => {
    e.preventDefault();
    const name = todoForm['todo_name'].value;
    const url = todoForm['todo_url'].value;
    const description = todoForm['todo_description'].value;

    await createTask(name, url, description); // Llamo a mi función create
    todoForm.reset(); // Reseteamos los campos
});
Enter fullscreen mode Exit fullscreen mode

🟨 Como el campo de URL es opcional, entonces coloco un condicional if in line para insertar o no una etiqueta a.
Estas tareas se visualizan utilizando window.addEventListener('DOMContentLoaded', ... ), dando uso del método onSnapshot que nos brinda Firebase para actualizar únicamente si hay nuevos datos.
Visualmente nos quedaría así:
Leer tareas

2. Eliminemos datos

🟨 Tener tantas tareas es medio molesto, no? Ahora agreguemos dos botones: Eliminar y Editar. Ahora solo laburaremos para el botón Eliminar.
Estos botones los agregamos dentro de innerHTML que usamos al leer datos, quedaría así (agrego un par de estilos de Bootstrap de paso):

const deleteTask = id => db.collection('tasks').doc(id).delete();

window.addEventListener('DOMContentLoaded', async (e) => {
    getTasks((querySnapshot) => {
        taskToDo.innerHTML = '';
        querySnapshot.forEach(doc => {
            const if_url = `<a href="${doc.data().url}">URL de tarea</a>` 
            taskToDo.innerHTML += `
                <div class="card my-2 p-2">
                    <h4>${doc.data().name}</h4>
                    <p>${doc.data().description}</p>
                    ${doc.data().url ? 
                        if_url
                        : ''
                    }
                    <div>
                        <button class="btn btn-secondary btn-edit" data-id="${doc.id}">Editar</button>
                        <button class="btn btn-primary btn-delete" data-id="${doc.id}">Eliminar</button>
                    </div>
                </div>
            `;

            const deleteButtons = document.querySelectorAll('.btn-delete');
            deleteButtons.forEach(btn => {
                btn.addEventListener('click', async (e) => {
                    await deleteTask(e.target.dataset.id);
                })
            })
        });
    });
});
Enter fullscreen mode Exit fullscreen mode

🟨 Al leer cada documento, guardamos los datos en doc. Con doc.data() obtenemos la información que guardamos y con doc.id obtenemos el id generado para cada documento. Esto último nos sirve para identificar cada par de buttons.
Cuando escucho los eventos click en los buttons Eliminar, me fijo el id puesto en data-id y llamo a la función deleteTask para eliminar la tarea del button accionado.
Nos queda algo así:
Eliminar tareas

3. Editemos datos

🟨 Para terminar, tenemos que crear la funcionalidad de editar las tareas para hacer. Es similar a lo que hicimos para eliminar, solo que deseamos que nos aparezca los datos en el formulario y luego enviar los datos actualizados. A modo de listado de lo que debemos implementar, sería:

  • Obtener el id del botón accionado
  • Cambiar el texto del botón del form por "Editar"
  • Obtener los valores de cada campo del form
  • Enviar estos datos a Firebase, usando el id del botón
  • Cambiar el texto del botón del form por "Guardar"

🟨 Teniendo en cuenta esto, debemos crear un par de variables para cambiar entre el estado Editar y Guardar, las cuales llamaremos editState y id.
El código final sería este:

const db = firebase.firestore();

const todoForm = document.getElementById('todo_form');
const taskToDo = document.getElementById('task_todo');

let editState = false;
let id = '';

const createTask = (name, url, description) => {
    db.collection('tasks').doc().set({
        name,
        url,
        description
    });
};

const getTask = id => db.collection('tasks').doc(id).get();

const getTasks = (callback) => db.collection('tasks').onSnapshot(callback);

const deleteTask = id => db.collection('tasks').doc(id).delete();

const updateTask = (id, updatedTask) => db.collection('tasks').doc(id).update(updatedTask);

window.addEventListener('DOMContentLoaded', async (e) => {
    getTasks((querySnapshot) => {
        taskToDo.innerHTML = '';
        querySnapshot.forEach(doc => {
            const if_url = `<a href="${doc.data().url}">URL de tarea</a>` 
            taskToDo.innerHTML += `
                <div class="card my-2 p-2">
                    <h4>${doc.data().name}</h4>
                    <p>${doc.data().description}</p>
                    ${doc.data().url ? 
                        if_url
                        : ''
                    }
                    <div>
                        <button class="btn btn-secondary btn-edit" data-id="${doc.id}">Editar</button>
                        <button class="btn btn-primary btn-delete" data-id="${doc.id}">Eliminar</button>
                    </div>
                </div>
            `;

            const deleteButtons = document.querySelectorAll('.btn-delete');
            deleteButtons.forEach(btn => {
                btn.addEventListener('click', async (e) => {
                    await deleteTask(e.target.dataset.id);
                })
            })

            const editButtons = document.querySelectorAll('.btn-edit');
            editButtons.forEach(btn => {
                btn.addEventListener('click', async (e) => {
                    const doc = await getTask(e.target.dataset.id);
                    const task = doc.data();

                    editState = true;
                    id = doc.id;

                    todoForm['todo_name'].value = task.name;
                    todoForm['todo_url'].value = task.url;
                    todoForm['todo_description'].value = task.description;
                    todoForm['btn_todo_form'].innerHTML = 'Editar';
                })
            })
        });
    });
});

todoForm.addEventListener('submit', async e => {
    e.preventDefault();
    const name = todoForm['todo_name'].value;
    const url = todoForm['todo_url'].value;
    const description = todoForm['todo_description'].value;

    if (!editState) {
        await createTask(name, url, description); // Llamo a mi función create
    } else {
        await updateTask(id, {name, url, description});
        editState = false;
        id = '';
        todoForm['btn_todo_form'].innerHTML = 'Guardar';
    }

    todoForm.reset(); // Reseteamos los campos
});
Enter fullscreen mode Exit fullscreen mode

Al apretar uno de los botones Editar de las tareas, tenemos esto:
Editar tarea


Ahora mismo tenemos una aplicación web funcionando. Hay varias cosas para editar y que en esta serie de artículos no tendremos en cuenta:

  • Cualquiera puede editar, guardar y eliminar datos.
  • No hay autenticación.
  • Diseño no es responsive.
  • Otras más pero no graves. Si leemos la documentación de Firebase, podemos solucionar gran parte del problema (agregando autenticación, reglas en la DB y demás).

En el próximo y último artículo, haremos el deploy para tenerlo en línea!

Top comments (3)

Collapse
 
jk9maker profile image
JK9maker

excelente, tengo una duda. se puede variar el valor del filtro que muestra en la siguiente linea
const getTasks = (callback) => db.collection('tasks').onSnapshot(callback);

por ejemplo:
const getTasks = (callback) => db.collection('tasks').where("aqui la parte que se pueda variar desde un input para el filtro").onSnapshot(callback);

excelente explicado saludos.

Collapse
 
sturpin profile image
Sergio Turpín

Muy bien explicado, enhorabuena Javier 👌🏻😉

Collapse
 
javicerodriguez profile image
Javier Rodriguez

Muchas gracias Sergio! 😄

Some comments have been hidden by the post's author - find out more