BootstrapVue Form Input and Vuex
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:
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.