Filtrar información en una app de hábitos requiere algo más que un filter() suelto: necesitas un formulario bien construido, buenas prácticas de accesibilidad y una forma limpia de extraer los datos. Aquí verás cómo montar un filtro dinámico con FormData que se conecte al render de tu proyecto sin recargar la página.
Cómo estructurar el modal de filtros en HTML
El punto de partida es agregar un botón nuevo llamado Filter Modal, siguiendo el mismo patrón que ya usaste con el botón de exportar. Cada modal en el proyecto tiene su estructura, y este no es la excepción [00:38].
Para ahorrar tiempo, el boilerplate del modal está disponible en un archivo templates.md que puedes copiar y pegar. El markup es sencillo: un header, un botón para cerrar y el contenido del formulario. Pero hay un detalle que cambia todo respecto a los otros modales del proyecto.
Los formularios anteriores, como el de register habit, no envuelven sus inputs en un elemento <form>. Van pidiendo campo por campo el valor de cada input. En este filtro vas a hacerlo diferente: englobar todo dentro de un <form> con su identificador, para manipular los datos de forma mucho más limpia.
¿Por qué usar un elemento form en lugar de inputs sueltos? Un <form> te permite capturar todos los valores de una sola vez con FormData, en vez de consultar input por input. Si tienes 20 campos, te ahorras 20 líneas de código.
Por qué usar type="button" en botones dentro de un form
Aquí entra una de las mejores prácticas de accesibilidad en HTML más olvidadas. Cuando tienes un botón dentro de un <form>, el navegador asume por defecto que ese botón envía el formulario [02:24].
Eso significa que si tu botón de cancelar no tiene un type definido, al hacer clic va a disparar el envío del formulario completo. Un bug silencioso que muchos desarrolladores han pasado horas debugueando.
La regla es simple:
- El botón que envía la información lleva
type="submit".
- Cualquier otro botón dentro del form lleva
type="button".
- Siempre define el
type explícito, no lo dejes al azar.
Cómo capturar todos los valores del formulario con FormData
Antes de procesar la información, cada campo del formulario necesita un atributo name, no solo un id. El name es la clave con la que ese valor va a quedar registrado en el objeto final. Sin name, el campo no aparece.
Una vez que el HTML está listo, seleccionas el formulario por su id (filterForm) y escuchas el evento submit, no un click. Escuchar el submit es más semántico y accesible: respeta el flujo natural del formulario cuando el usuario presiona Enter o clickea el botón principal.
javascript
filterForm.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const values = Object.fromEntries(formData);
console.log(values);
});
El e.preventDefault() es crítico. Sin él, el navegador recarga la página completa al enviar el formulario y pierdes todo el estado de la aplicación [05:33].
Qué hace exactamente Object.fromEntries con FormData
new FormData(e.target) toma el formulario completo y arma una estructura con todos los pares clave-valor. Luego, Object.fromEntries() convierte esa estructura en un objeto plano de JavaScript, listo para usar.
El resultado es un objeto donde cada propiedad corresponde al name de un campo:
javascript
{
filterFrequency: "weekly",
filterDate: "2024-03-15"
}
Con esto tienes todo el estado del formulario en una sola llamada, sin importar si son 2 campos o 20.
¿Qué diferencia hay entre id y name en un input? El id sirve para seleccionar el elemento desde JavaScript o CSS. El name es lo que FormData usa como clave para armar el objeto de valores. Si un input no tiene name, no aparece en el FormData.
Cómo aplicar el filtro dinámico al array de hábitos
Con el objeto de valores en mano, el siguiente paso es aplicar un filter() sobre la lista de hábitos. Este es el reto que se propone al final: usar el formulario para filtrar el array de forma dinámica e imprimir el resultado en consola.
La lógica es similar a filtros anteriores, pero con una diferencia clave: los criterios no están fijos en el código, vienen del formulario. Eso lo convierte en un filtro dinámico donde cada hábito debe cumplir con los valores presentes en el objeto de filtros.
Algunas ideas para atacar el reto:
- Recorrer las claves del objeto de valores con
Object.keys().
- Comparar cada hábito contra los valores del formulario.
- Ignorar campos vacíos para no filtrar de más.
El render reactivo, es decir, que la lista visible en pantalla se actualice sola cuando cambia el filtro, se resuelve con un patrón reactivo que se implementa después. Por ahora, el objetivo es dejar la lógica del filtro funcionando y comprobarla con un console.log.
¿Cómo te fue con el reto? Cuéntame en los comentarios cómo estructuraste tu función de filtrado dinámico.