Filtros dinámicos con FormData en JavaScript

Resumen

Aprender a construir un filtro dinámico en JavaScript implica algo más que escribir un filter(): requiere conectar un formulario HTML con la lógica de tu aplicación de forma limpia. Aquí verás cómo crear un formulario de filtros, capturar todos sus valores con FormData y aplicar buenas prácticas de accesibilidad en un proyecto real de gestión de hábitos.

Esta guía te sirve si ya entiendes el método filter y quieres llevarlo al DOM con un patrón profesional, sin tener que pedir valor por valor en cada input.

Cómo se estructura un modal de filtros en HTML

El punto de partida es replicar el patrón de modales que ya existe en el proyecto, como el Create Habit Modal o el Register Habit Modal, pero añadiendo un nuevo botón llamado Filtrar y un nuevo contenedor: el Filter Modal [01:14].

Dentro de ese modal vas a colocar un <form> con un identificador propio, por ejemplo filterForm. Englobar todos los campos en un formulario te permite manipularlos como una unidad y no como inputs sueltos.

Por qué cada botón debe tener un type explícito

Una trampa común en HTML: si un botón vive dentro de un <form> y no declara su type, el navegador asume que es de tipo submit. Eso significa que apretar Cancelar puede terminar enviando el formulario completo [02:36].

La regla práctica es simple:

  • Al botón que envía la información dale type="submit".
  • A todos los demás botones del formulario dales type="button".
  • Aplica esto siempre, incluso si crees que no hace falta.

¿Por qué un botón sin type causa errores en formularios? Porque dentro de un <form> el valor por defecto es submit. Sin un type="button" explícito, cualquier botón puede disparar el envío del formulario sin que tú lo quieras.

No sabes cuántas horas se han perdido depurando un comportamiento raro cuyo único origen era un type faltante.

Por qué cada input necesita un atributo name

Si quieres recoger todos los valores de una sola pasada, no basta con el id. Cada select, input o campo de fecha debe tener un atributo name, porque ese name es la clave con la que aparecerá el dato en el objeto resultante [03:48].

Por ejemplo, un campo de fecha quedaría así: <input type="date" id="filterDate" name="filterDate">.

Cómo capturar todos los valores con FormData y Object.fromEntries

En los formularios anteriores del proyecto, el código pregunta input por input: cuál es el valor de habitName, cuál es el de habitFrequency, y así sucesivamente. Funciona, pero escala mal. Si tienes 20 campos, escribes 20 líneas casi idénticas [05:35].

La alternativa más limpia es escuchar el evento submit del formulario y construir un FormData directamente desde event.target.

javascript const form = document.getElementById('filterForm');

form.addEventListener('submit', (event) => { event.preventDefault(); const formData = new FormData(event.target); const values = Object.fromEntries(formData); console.log(values); });

Dos detalles que importan en ese bloque:

  • event.preventDefault() evita que el navegador recargue la página al enviar el formulario, algo que destruiría tu estado actual [05:00].
  • Object.fromEntries(new FormData(event.target)) convierte el formulario en un objeto plano con la forma { filterFrequency: 'weekly', filterDate: '2024-05-01' }.

¿Qué hace event.preventDefault en un submit? Cancela el comportamiento por defecto del formulario, que es recargar la página al enviarse. Sin él, perderías el estado de tu aplicación en cada filtro.

¿Qué devuelve Object.fromEntries con un FormData? Devuelve un objeto donde cada name del formulario es una clave y su valor es lo que el usuario seleccionó. Si tienes 20 inputs, obtienes las 20 parejas en una sola línea.

Escuchar submit en lugar de click también es una mejora de accesibilidad: respetas la forma en que los lectores de pantalla y el teclado activan los formularios.

Cómo conectar los valores del formulario con filter

Una vez que tienes el objeto values, ya tienes el estado completo del filtro. Desde ahí puedes ir al array de hábitos y aplicar filter() usando esas claves como condición [08:38].

La idea es que el filtro sea dinámico: si el usuario seleccionó frecuencia weekly y una fecha específica, tu filter debe contemplar ambas condiciones en un solo recorrido del array. Es un filtro compuesto, parecido a cuando filtras productos por categoría y rango de precio al mismo tiempo.

Por ahora el resultado puedes imprimirlo en consola con un console.log. La parte de re-renderizar la lista en pantalla viene después, con un patrón reactivo que conecta el estado del filtro con la vista.

Qué reto seguir antes de la solución

El ejercicio concreto: toma el objeto values que sale del formulario, recorre la lista de hábitos con filter y devuelve solo los que cumplan con todas las claves activas del filtro. Imprímelo en consola y compara tu solución con la del siguiente commit.

Cuéntame en los comentarios cómo resolviste el filtro compuesto y qué condiciones añadiste para manejar campos vacíos.