Algolia vue instantsearch dynamically set search query

2k views Asked by At

I have a search bar on the root component (App.vue). I want to enter a query and on @keyup.enter, it should redirect to the Search component view with the v-text-field input value. Redirection is used by using $router.replace because users might search for a different keyword from within the same route.

The code below work but only ONCE. If I enter a new search term, the URL changed but the results stay the same.

App.vue

<template>
  <div>
    <header>
      <v-text-field @keyup.enter="goToSearchPage($event)"></v-text-field>
    </header>
    <v-main>
      <router-view></router-view>
    </v-main>
  </div>
</template>
 <script>
    export default {
      methods: {
        goToSearchPage($event) {
          this.$router.replace({
            name: "Search",
            query: { q: $event.target.value }
          });
        }
      }
    };
    </script>

views/Search.vue

<template>
  <div>
    <ais-instant-search
      index-name="dev_brunjar_products"
      :search-client="searchClient"
      :search-function="searchFunction"
    >
      <ais-hits>
        <ul slot-scope="{ items }">
          <li v-for="item in items" :key="item.objectID">
            {{ item.name }}
          </li>
        </ul>
      </ais-hits>
    </ais-instant-search>
  </div>
</template>

<script>
import algoliasearch from "algoliasearch/lite";

export default {
  data() {
    return {
      searchClient: algoliasearch(
        process.env.VUE_APP_ALGOLIA_APP_ID,
        process.env.VUE_APP_ALGOLIA_SEARCH_KEY
      )
    };
  },
  methods: {
    // According to Algolia's doc, this should be inside data instead of methods
    // https://www.algolia.com/doc/api-reference/widgets/instantsearch/vue/#widget-param-search-function
    // But doing so, I wouldn't be able to get this.$route.query.q
    searchFunction(helper) {
      var query = this.$route.query.q;

      if (query) {
        helper.setQuery(query).search();
      }
    }
  }
};
</script>

What I've tried

Did a hack-ish way (Test 1) to solve it but didn't work (which I'm glad, because it doesn't feel right). Below was the non-working code addition to the Search component. Created computed & watch property of query which get its data from this.$route.query.q and algoliaHelper data assigned with AlgoliaSearchHelper when the searchFunction first load.

When I typed a new search term, the watcher works and the query indeed changed. Despite that, calling the helper and setting its query with the new term within the watcher did not change the results from Algolia.

Then I used Routing URLs (Test 2) to the ais-instant-search and it still didn't solve the issue. Maybe I'm implementing it wrong? I really tried to understand Algolia's doc and it's just too hard to digest.

views/Search.vue - Test 1 (Failed)

<template>
  <div>
    <ais-instant-search
      index-name="dev_brunjar_products"
      :search-client="searchClient"
      :search-function="searchFunction"
    >
      <ais-hits>
        <ul slot-scope="{ items }">
          <li v-for="item in items" :key="item.objectID">
            {{ item.name }}
          </li>
        </ul>
      </ais-hits>
    </ais-instant-search>
  </div>
</template>

<script>
import algoliasearch from "algoliasearch/lite";

export default {
  data() {
    return {
      searchClient: algoliasearch(
        process.env.VUE_APP_ALGOLIA_APP_ID,
        process.env.VUE_APP_ALGOLIA_SEARCH_KEY
      ),
      algoliaHelper: null
    };
  },
  computed: {
    query() {
      return this.$route.query.q;
    }
  },
  watch: {
    query(newQuery) {
      this.algoliaHelper.setQuery(newQuery).search();
    }
  },
  methods: {
    searchFunction(helper) {
      if (!this.algoliaHelper) {
        this.algoliaHelper = helper;
      }

      if (this.query) {
        helper.setQuery(this.query).search();
      }
    }
  }
};
</script>

views/Search.vue - Test 2 (Failed)

<template>
  <div>
    <ais-instant-search
      index-name="dev_brunjar_products"
      :search-client="searchClient"
      :search-function="searchFunction"
      :routing="routing"
    >
      <ais-hits>
        <ul slot-scope="{ items }">
          <li v-for="item in items" :key="item.objectID">
            {{ item.name }}
          </li>
        </ul>
      </ais-hits>
    </ais-instant-search>
  </div>
</template>

<script>
import { history as historyRouter } from "instantsearch.js/es/lib/routers";
import { singleIndex as singleIndexMapping } from "instantsearch.js/es/lib/stateMappings";
import algoliasearch from "algoliasearch/lite";

export default {
  data() {
    return {
      searchClient: algoliasearch(
        process.env.VUE_APP_ALGOLIA_APP_ID,
        process.env.VUE_APP_ALGOLIA_SEARCH_KEY
      ),
      routing: {
        router: historyRouter(),
        stateMapping: singleIndexMapping("instant_search")
      }
    };
  },
  methods: {
    searchFunction(helper) {
      if (this.query) {
        helper.setQuery(this.query).search();
      }
    }
  }
};
</script>

I would appreciate it if you guys know how to solve this issue.

2

There are 2 answers

2
eunjae.lee On

https://codesandbox.io/s/github/algolia/doc-code-samples/tree/master/Vue%20InstantSearch/routing-vue-router?file=/src/views/Home.vue

This is an example using vue router. I guess this might be what you're looking for. Please let us know if it works for you.

0
gambrosino On

Hope you where able to solve this since it's been a long time since you asked. But, in order to make it work you have to put searchFunction(helper) inside data() as shown in the docs: https://www.algolia.com/doc/api-reference/widgets/instantsearch/vue/#widget-param-search-function