Introduction

Recently I have been playing with Vue (2.x) and using it to build a search bar. The UI looks a bit like this:

image

My goal is to render the search results in different tabs. Each tab is a different representation of the same results (tables, charts, etc.). This mandates that tabs share the same set of search results.

When users search a new keyboard in the global search bar, all tabs are updated with the new results.

To reuse the same search results, I need to store the results in a shared state, which leads me to Vuex.

To build the UI, I use BootstrapVue.

One tricky thing is to deal with the integration with Vuex because its form handling is tricky.

My solution

The Vue template code:

<template>
  <b-form @submit="search">
      <b-input-group prepend="Global Search">
        <b-form-input
          v-model="query"
          debounce="500"
          placeholder="Enter your keywords"
        ></b-form-input>
      </b-input-group>
    </b-form>
</template>

I use a two-way computed property.

<script>
import { mapState } from "vuex";

export default {
  name: "Homepage",
  computed: {
    query: {
      get() {
        return this.$store.state.query;
      },
      set(query) {
        this.$store.commit("updateQuery", { query: query });
      },
    },
    ...mapState(["results"]),
  },
  methods: {
    search(e) {
      e.preventDefault();
      this.$store.dispatch("search");
    }
  },
};
</script>

Then it’s my vuex store:

import Vuex from "vuex";
import api from './api';

const store = new Vuex.Store({
  state: {
    query: "",
    queryChanged: false,
    results: [],
  },
  mutations: {
    updateQuery(state, payload) {
      if (payload.query != state.query) {
        state.query = payload.query;
        state.queryChanged = true;
      }
    },
    updateResults(state, payload) {
      state.queryChanged = false;
      state.results = payload.results;
    },
  },
  actions: {
    search({ commit, state }) {
      if (state.queryChanged) {
        const results = api.search(state.query);
        commit("updateResults", { results });
      }
    },
  },
});

export default store;

Note: To do less API calls, I made a state to keep track of whether query has been changed. The store update the results only when query has been changed.

Alternative approach: v-model + local state

Another approach is to use the v-model + local state. In this case, you would have data, computed and methods:

data() {
  return { query: "" };
},
computed: mapState(["results"]),

methods: {
  search(e) {
    e.preventDefault();
    this.$store.dispatch("search", { query: this.query });
  }
}

This approach requires vuex store to update the query.

I don’t like this alternative because it means I always have to maintain the local query and the store state in sync, which comes for free in the my current approach.


Email Newsletter

I write about code and entrepreneurship. Sign up and get my updates straight to your inbox!



Junji Zhi

Senior Software Engineer.