Aprende a conectar el Observer Pattern con un motor de filtros multicriterio en JavaScript para que tu interfaz reaccione automáticamente a cada cambio de estado, sin renders manuales ni código acoplado. Esta guía te muestra cómo unir el FilterState, el HabitService y la UI en una arquitectura reactiva limpia.
¿Por qué necesitas un motor de filtros multicriterio?
Filtrar por un solo campo es trivial, pero las apps reales rara vez se quedan ahí. Piensa en un tracker de hábitos donde quieres combinar nombre, racha activa, fecha, frecuencia diaria o semanal y estado. Ahí un filter plano se queda corto y necesitas un motor que descarte por múltiples criterios al mismo tiempo.
La idea es separar responsabilidades: la UI modifica el FilterState, y el estado, de forma reactiva, dispara el render. Nadie llama al filtro a mano.
¿Qué es un motor de filtros multicriterio? Es una función que recibe una lista y un objeto de filtros, y aplica varias condiciones encadenadas (frecuencia, status, racha mínima, texto) devolviendo solo los elementos que cumplen todas.
¿Cómo conectar el HabitService con el FilterState? [01:30]
El primer cambio está en el servicio. HabitService ya no recibe solo el repository: ahora también recibe el FilterState por inyección de dependencias. Crear la instancia adentro sería más rápido, pero recibirla mantiene el servicio desacoplado y testeable.
Dentro del servicio aparecen dos métodos clave:
listFilteredHabits(), que toma la lista actual y aplica los filtros del estado.
applyFilters(habits, filters), un método privado que contiene el motor multicriterio.
getStateFilters(), una utilidad para exponer el estado cuando otra parte lo necesite.
En el constructor inyectas la nueva dependencia y listo: el servicio ya sabe leer filtros, pero todavía no reacciona a los cambios. Esa parte la resuelve el patrón.
¿Dónde se modifica el estado desde el formulario? [03:45]
En el listener del filter form el cambio es radical. Antes el evento llamaba al filtro directamente; ahora solo hace setFilters(values) con los datos del formulario. Nada más.
No hay render, no hay llamada al servicio, no hay manipulación del DOM. Solo modificas el estado. Esa es la esencia del Observer Pattern aplicado a UI: quien dispara el cambio no sabe quién va a reaccionar.
¿Cómo suscribir el render a los cambios del estado? [05:20]
Del lado de quien renderiza, te suscribes una sola vez al FilterState. Cada vez que el estado cambie, el callback recibe los filtros actuales y llama a renderHabits().
js
filterState.subscribe((filters) => {
renderHabits();
});
La ventaja es enorme: puedes tener varios subscribers en distintas zonas de la interfaz, todos reaccionando al mismo estado sin acoplarse entre sí. No tienes que rastrear manualmente dónde se actualiza la UI.
¿Qué hace subscribe en el Observer Pattern? Registra una función que se ejecutará cada vez que el sujeto observado notifique un cambio, permitiendo que múltiples partes de la app reaccionen al mismo evento.
¿Cómo adaptar el HTML al estado real de los filtros? [07:10]
El formulario original solo tenía dos campos, pero el FilterState espera cuatro: frecuencia, status, racha mínima y texto. En lugar de escribir el HTML a mano, puedes usar vibe coding con Cursor pasándole como contexto las líneas exactas del FilterState con Ctrl+L.
El prompt es directo: pedirle que modifique el filter form en el HTML para que coincida con el estado de los filtros. Si no usas Cursor, puedes lograr lo mismo con VS Code, Claude Code, Gemini o ChatGPT pegando el contexto manualmente. Lo cómodo del editor con IA integrada es no saltar entre apps.
¿Cómo implementar el motor applyFilters? [09:30]
Dentro de applyFilters se descartan elementos según el estado. Cada criterio se evalúa de forma independiente y, si el filtro no está activo, retorna true para no descartar nada.
- Frecuencia: compara contra
daily o weekly.
- Status de racha: usa programación de objetos para evaluar si la racha está activa.
- Racha mínima: chequea un valor numérico.
- Texto: aplica
includes() para coincidencias parciales en el nombre.
En renderHabits reemplazas la lista cruda por listFilteredHabits(), así el render siempre refleja el estado actual de los filtros.
¿Por qué el subscribe no se ejecutaba? [12:40]
Al probar, el formulario disparaba setFilters, los valores llegaban por consola, pero el subscribe nunca corría. El render no reaccionaba.
El bug era pequeño y revelador: faltaba llamar a notify() dentro de setFilters. Sin esa notificación, el sujeto cambia su estado pero nunca avisa a sus observadores. La regla del patrón es clara: cada mutación del estado debe terminar con this.notify().
js
setFilters(newFilters) {
this.filters = { ...this.filters, ...newFilters };
this.notify();
}
Lo mismo aplica en resetFilters y en cualquier setter individual. Sin notify, el patrón está incompleto.
¿Qué hace notify en el patrón Observer? Recorre la lista de suscriptores y ejecuta cada callback pasándoles el nuevo estado, cerrando el ciclo entre el cambio y la reacción.
¿Cómo se ve el flujo completo en acción? [15:10]
Con notify en su lugar, el flujo queda redondo. Creas un Hábito 1 diario y un Hábito 2 semanal. Activas el filtro daily y solo aparece el primero. Cambias a weekly y aparece el segundo. Escribes texto que coincide con uno solo y la lista se reduce a esa coincidencia. Buscas algo que no existe y la lista queda vacía.
La interfaz reacciona sola, sin un solo renderHabits() llamado a mano desde el evento del formulario. Ese es el valor real de unir el Observer Pattern con un motor multicriterio: código desacoplado, varios suscriptores posibles y un único punto de verdad para los filtros.
¿Ya identificaste en tu propio proyecto un lugar donde el patrón Observer te ahorraría llamadas manuales al render? Cuéntalo en los comentarios.