Filtrado Client-Side en Vue y Laravel

Video thumbnail

Quería mostrarte una implementación que considero interesante: un filtro. Usualmente, consideramos que un filtro es una operación simple, pero su implementación cambia drásticamente si se realiza en el servidor o en el cliente.

1. El Enfoque Clásico: Filtrado en el Servidor (Laravel)

En el lado del servidor, el filtrado es elegante y sencillo. Usualmente, se compone una consulta (query) en función de los parámetros recibidos, como en este ejemplo con Laravel Eloquent:

$posts = Post::orderBy($this->sortColumn, $this->sortDirection);
if ($this->type) {
   $posts->where('type', $this->type);
}
// ... más condiciones
if ($this->category_id) {
   $posts->where('category_id', $this->category_id);
}
$posts = $posts->paginate(20);

Aquí, simplemente componemos el query: si un parámetro está definido, aplicamos la restricción (where); si no, lo ignoramos. La última operación es paginar, y la base de datos se encarga de devolvernos la data filtrada y limpia.

2. El Problema del Filtrado Client-Side (Vue)

El problema surge cuando la data ya está cargada en el cliente (como en la sección de libros, donde tenemos toda la data disponible). No tiene sentido hacer otra petición al servidor, ya que el filtro nunca devolverá más data de la que ya poseemos.

El Riesgo de Remover la Data

La reactividad de Vue nos juega una mala pasada. Si filtramos los libros para ver solo los "Gratis" y eliminamos los de "Pago" del array principal, cuando el usuario quiera cambiar el filtro a "Pago", ya no habrá data que mostrar.

Ejemplo: Si tengo 20 libros, aplico filtro "Gratis" (quedan 5), y elimino los otros 15. Si luego cambio a "Pago", no aparecerá nada, aunque antes había 15 libros de pago.

Filtro Sencillo vs. Filtro Complejo

Con un filtro sencillo (por ejemplo, un solo checkbox para solo mostrar cursos gratis), podíamos resolverlo en una sola línea usando el método filter de JavaScript con un condicional corto:

<book-item v-for="c in onlyBooksFree ? books.filter((c) => c.price == '0.00') : books" ... >

Sin embargo, cuando agregamos dos o más filtros (por Precio, Idioma, Tecnología, etc.), la solución de una línea se vuelve imposible. Se necesita un mecanismo que aplique múltiples condiciones de forma no excluyente y que permita al usuario cambiar de filtro sin perder la data original.

⚙️ 3. La Solución Escalable: Función Centralizada y Propiedad show

Mi solución para manejar filtros complejos y múltiples en el cliente fue implementar una lógica más elaborada con funciones separadas y observadores (watch) en Vue.

A. Observación de Cambios con watch

En lugar de confiar en event listeners complejos en el template, usamos watch para observar los cambios en los parámetros del filtro (onlyBooksPayFree, idioma, etc.). Cada vez que un filtro cambia, se llama a la función principal filterData().

watch: {
   onlyBooksPayFree() {
       this.filterData()
   }
   // ... otros watchers para idioma, etc.
}

B. Evitar la Eliminación: Propiedad show

Para evitar perder la data original, opté por no eliminar objetos del array books. En su lugar, inyecto una nueva propiedad local (no parte del modelo del book) llamada show (booleano) a cada objeto del array.

  • cleanFilterData(): Lo primero es limpiar los filtros. Recorremos todo el array y establecemos this.books[i].show = true para todos. Partimos de un listado donde todos los libros se muestran.
  • filterData(): Llama a cleanFilterData() y luego comienza a aplicar los filtros que estén activos:
filterData() {
   this.cleanFilterData() // Todos tienen show: true
   if (this.onlyBooksPayFree == 'pay') {
       this.applyFilterBookPay()
   } else if (this.onlyBooksPayFree == 'free') {
       this.applyFilterBookFree()
   }
   // ... Lógica para el filtro de Idioma
}

Lógica de Filtro (applyFilterBookFree): La función de filtro recorre los libros y, si un libro NO cumple la condición, establece this.books[i].show = false.

applyFilterBookFree() {
   for (let i = 0; i < this.books.length; i++) {
       if (this.books[i].price != '0.00') { // Si NO es gratis
           this.books[i].show = false // Ocultar
       }
   }
}

C. La Ventaja del “Ocultamiento Secuencial”

Esta aproximación permite aplicar los filtros de forma secuencial (un filtro tras otro, en cascada) sin preocuparse si el elemento ya fue ocultado por un filtro anterior (el AND lógico).

  • Si un libro es ocultado por el filtro de "Precio" (show = false), el siguiente filtro de "Idioma" ya no tiene que preocuparse por desocultarlo.
  • Solo con que una de las condiciones no se cumpla a lo largo de la cadena, el flag show se establece en false, y el elemento permanece oculto al final.

Esta solución, aunque más compleja de lo que parece a primera vista, es escalable y resuelve el problema de la pérdida de data en los filtros client-side.

Nota: Otra opción sería trabajar con un array copia y remover los objetos que no cumplen la condición, pero la solución del flag show me resultó más sencilla para manipular la visibilidad sin afectar el array de datos original.

Acepto recibir anuncios de interes sobre este Blog.

Te muestro como implementé un filtro múltiple para una aplicación realizada en Vue y Laravel

| 👤 Andrés Cruz

🇺🇸 In english