Implementing a Multiple Filter in Vue, Experience

Video thumbnail

I wanted to show you an implementation that I consider interesting: a filter. We usually consider a filter to be a simple operation, but its implementation changes drastically if it's done on the server or on the client.

1. The Classic Approach: Server-Side Filtering (Laravel)

On the server side, filtering is elegant and simple. A query is usually composed based on the received parameters, as in this example with 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);

Here, we simply compose the query: if a parameter is defined, we apply the restriction (where); if not, we ignore it. The last operation is pagination, and the database handles returning the filtered and clean data to us.

2. The Problem with Client-Side Filtering (Vue)

The problem arises when the data is already loaded on the client (like in the books section, where we have all the data available). It doesn't make sense to make another request to the server, as the filter will never return more data than what we already possess.

The Risk of Removing Data

Vue's reactivity plays a trick on us. If we filter the books to only see the "Free" ones and remove the "Paid" ones from the main array, when the user wants to change the filter to "Paid," there will be no data left to show.

Example: If I have 20 books, I apply the "Free" filter (5 remain), and I remove the other 15. If I then change to "Paid," nothing will appear, even though there were 15 paid books before.

Simple Filter vs. Complex Filter

With a simple filter (for example, a single checkbox to only show free courses), we could solve it in one line using JavaScript's filter method with a short conditional:

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

However, when we add two or more filters (by Price, Language, Technology, etc.), the one-line solution becomes impossible. A mechanism is needed that applies multiple conditions non-exclusively and allows the user to change filters without losing the original data.

⚙️ 3. The Scalable Solution: Centralized Function and the show Property

My solution for handling complex and multiple client-side filters was to implement a more elaborate logic with separate functions and watchers in Vue.

A. Observing Changes with watch

Instead of relying on complex event listeners in the template, we use watch to observe changes in the filter parameters (onlyBooksPayFree, language, etc.). Every time a filter changes, the main filterData() function is called.

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

B. Avoiding Deletion: The show Property

To avoid losing the original data, I chose not to remove objects from the books array. Instead, I inject a new local property (not part of the book model) called show (boolean) into each object in the array.

  • cleanFilterData(): The first thing is to clear the filters. We loop through the entire array and set this.books[i].show = true for all. We start with a list where all books are shown.
  • filterData(): Calls cleanFilterData() and then begins to apply the active filters:
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
}

Filter Logic (applyFilterBookFree): The filter function loops through the books and, if a book DOES NOT meet the condition, it sets 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. The Advantage of “Sequential Hiding”

This approach allows filters to be applied sequentially (one filter after another, in cascade) without worrying about whether the element has already been hidden by a previous filter (the logical AND).

  • If a book is hidden by the "Price" filter (show = false), the next "Language" filter no longer has to worry about unhiding it.
  • If only one of the conditions is not met throughout the chain, the show flag is set to false, and the element remains hidden at the end.

This solution, although more complex than it initially appears, is scalable and solves the problem of data loss in client-side filters.

Note: Another option would be to work with a copy array and remove the objects that do not meet the condition, but the show flag solution was simpler for me to manipulate visibility without affecting the original data array.

I agree to receive announcements of interest about this Blog.

I show you how I implemented a multiple filter for an application made in Vue and Laravel

| 👤 Andrés Cruz

🇪🇸 En español