Vuejs 3 Search Bar Using Computed Properties [Composition API]

Javascript Vue.js

Whenever we have a list of items such as products, it’s obvious to have a search items functionality on the client-side for a better user experience.

In Vue 3 Composition API, we can easily create a search bar using computed properties.

In fact, this is one of the perfect cases for utilizing computed properties.

I assume you already know how to get Up and Running With Vue JS 3 Project Using Vue CLI

Let’s say I have a ProductList.vue page-based component that will show a list of products that I want to add search functionality to it.

ProductList.vue

<template>
</template>

<script>
export default {
  setup() {},
};
</script>

Get Products Data From Firebase

Let’s make an HTTP request to a server to get a list of products.

I use Firebase in this example however you do not need to know Firebase to follow along.

It makes more sense to get data by making actual HTTP requests rather than just creating a list of items array within the component.

If you want to know more about how to get started with Firebase in your Vue project, check this link here.

I’ve already added a few products to the Cloud Firestore which is one of the databases Firebase offers and it looks like the image below.

It has a collection called products that contains a list of product documents.

As you can see, each product document has a few properties:

  • Title
  • Upc
  • Brand, and so on.

Nothing fancy!

Now… let’s get the data into the component.

First, import firebase at the top as well as import onMounted and reactive from vue.

The onMounted() method is one of the lifecycle methods in Vue 3 Composition API and it’ll be called when the Vue component is added to the DOM.

Inside the setup() function, initialize the products variable with an empty array which will later have all of the products.

import firebase from "firebase";
import { onMounted, reactive } from "vue";
export default {
  setup() {
    const products = reactive([]);
    onMounted(async () => {
      try {
        const productsSnap = await firebase
          .firestore()
          .collection("products")
          .get();
        productsSnap.forEach((doc) => {
          products.push(doc.data());
        });
      } catch (e) {
        console.log("Error Loading Products");
      }
    });
    return { products };
  },
};

There are two ways to define reactive variables in Vue 3 Composition API. I prefer using reactive over ref when possible but sometimes ref is inevitable.

Recommended
Must-Know Ref vs Reactive Differences In Vue 3 Composition API

Then make a request to the Firebase products collection and loop through the documents then push them to the products array.

Finally, setup() functions returns the products array so that template will have access to it!

Pretty straight forward!

Normally, I use a reusbale module file to do all the CRUD async operations but for this example I put everything in a single component to make this article shorter.

You can learn more about how to create Reusable Modules as well as Components In Vue 3 Composition API

Show A List of Products

Loop through the products array and show the title, upc other information in the template.

<template>
  <div class="ui cards" style="margin: 10px">
    <div
      class="card ui fluid"
      v-for="product in products"
      :key="product.id"
      style="margin: 0"
    >
      <div class="content">
        <img class="right floated mini ui image" :src="product.imageURL" />
        <div class="header">{{ product.title }}</div>
        <div class="meta">
          {{ product.upc }} | {{ product.weight }} Kg |
          {{ product.itemsperpack }} pack
        </div>
      </div>
    </div>
  </div>
</template>

And the output will look like this:

I use Semantic UI CSS framework to speed up the UI designing process – feel free to use your own CSS framework.

Add Search Bar UI 

As you know, in Vue 3 we can create multiple sibling elements inside template tags.

So, just add the search input field right above the product list HTML code.

<template>
  <div class="ui icon input" style="width: 100%">
    <input type="text" placeholder="Search..." />
    <i class="search icon"></i>
  </div>
  <div
      class="card ui fluid"
      v-for="product in products"
      :key="product.id"
      style="margin: 0"
    >
     ... 
</template>

And the view will look like this:

Recommended
Vue JS 3 Composition API Form Validation – Signup & Login Pages

Implement Search Functionality Using Computed Property

You could attach a keyup event to the input field and implement the search functionality that way but that would be tedious.

The better way is using computed properties.

Let’s see that in action.

Define a varibale called searchQuery inside the setup() function.

const searchQuery = ref("");

Then, bind it to the search input field using v-model directive.

<input type="text" placeholder="Search..." v-model="searchQuery" />


Finally, create SearchedProducts computed property which filters all the items from the products array that match the searchQuery text and returns only the matched items to the searchedProducts array.

const searchedProducts = computed(() => {
      return products.value.filter((product) => {
        return (
          product.title
            .toLowerCase()
            .indexOf(searchQuery.value.toLowerCase()) != -1
        );
      });
});

It’s important to convert both product title and searchQuery text to lowercase before checking the match using indexOf method. 

Finally, setup() function returns searchedProducts.

setup() {
  ...
  return { searchedProducts, searchQuery };
}

Now all I have to do is get rid of products from the return statement and change products to searchedProducts in the v-for loop signature in the template.

<template>
  <div class="ui cards" style="margin: 10px">
    <div
      class="card ui fluid"
      v-for="product in searchedProducts"
      :key="product.id"
      style="margin: 0"
    >
      ...
    </div>
  </div>
</template>

And it will work as expected.

So what’s happening?

When the component is mounted, Firebase fetches data from Cloud Firestore and pushes it to the products array.

At the same time, searchedProducts computed property filters data that match the searchQuery which will be an empty string for the first time.

That means, all the products will be added to the searchedProducts array for the first time.

When we start typing in the search bar, the searchedProducts computed property will have filtered products which we will then loop through in the template.

This way we do not have to have two sets of template code – one for initial load and the other one for filtered result.

Sometimes, you may run into a situation where the computed property starts filtering before data fetch is completed.

In that case, all we have to do is to replace onMounted() life cycle method to onBeforeMount().

Conclusion

Now, you can see how easy it’s to implement search functionality to a list of items using computed properties in Vue 3 Composition API.

Source code available here

Recommended
Vue JS 3 Composition API Form Validation – Signup & Login Pages