Concepts / Building Search UI / Autocomplete
May. 10, 2019

Autocomplete

Overview

A common pattern in search is to implement a search box with an autocomplete as a first step of the search experience. Vue InstantSearch doesn’t come with a built-in widget for the autocomplete. But we have a slotted component ais-autocomplete that lets you use an external autocomplete component.

In this guide we will cover a search box which displays an autocomplete menu linked to a results page.

We won’t cover the usage of the connector in a multi-index context in this guide. There is a dedicated section about that in the multi-index search guide. You can find the source code of both examples on GitHub.

Results page with autocomplete

This is focused on integrating a ais-search-box with an autocomplete linked to a results page. To implement this we use the library Vue Autosuggest that provides a component to create an autocomplete menu. Once we have this component, we need to wrap it with our ais-autocomplete component. The component exposes three interesting props for this use case: indices, and refine. indices contains the list of suggestions, and refine is a function that takes a query to retrieve our relevant suggestions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<template>
  <div>
    <ais-instant-search
      :search-client="searchClient"
      index-name="demo_ecommerce"
    >
      <ais-configure
        :hitsPerPage="5"
        :restrictSearchableAttributes="['name']"
      />
      <ais-autocomplete>
        <template slot-scope="{ currentRefinement, indices, refine }">
          <vue-autosuggest
            :suggestions="indicesToSuggestions(indices)"
            @selected="onSelect"
            :input-props="{
              style: 'width: 100%',
              onInputChange: refine,
              placeholder: 'Search here…',
            }"
          >
            <template slot-scope="{ suggestion }">
              <ais-highlight
                :hit="suggestion.item"
                attribute="name"
                v-if="suggestion.item.name"
              />
              <strong>$ {{ suggestion.item.price }}</strong>
              <img :src="suggestion.item.image" />
            </template>
          </vue-autosuggest>
        </template>
      </ais-autocomplete>
    </ais-instant-search>
  </div>
</template>

<script>
import algoliasearch from 'algoliasearch/lite';
import { VueAutosuggest } from 'vue-autosuggest';

export default {
  components: { VueAutosuggest },
  data() {
    return {
      searchClient: algoliasearch(
        'YourApplicationID',
        'YourSearchOnlyAPIKey'
      ),
      query: '',
    };
  },
  methods: {
    onSelect(selected) {
      if (selected) {
        this.query = selected.item.name;
      }
    },
    indicesToSuggestions(indices) {
      return indices.map(({ hits }) => ({ data: hits }));
    },
  },
};
</script>

When we have our autocomplete component set up we can integrate it in our application. But for that we need to use two instances of InstantSearch. We use two instances because it allows us to configure the number of hits retrieved by the autocomplete differently than the number of results. The huge benefit of it is also to not have the query tied to both instances at the same time. It allows us to clear the suggestions but sill have the query applied on the second instance. Since we have two instances we need a way to sync the query between the two. We use the ais-configure to achieve this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
  <div>
    <!-- first ais-instant-search instance goes before this -->
    <ais-instant-search
      :search-client="searchClient"
      index-name="demo_ecommerce"
    >
      <!-- setting the query from the first instance on the second instance -->
      <ais-configure :query="query" />
      <ais-hits>
        <div slot="item" slot-scope="{ item }">
          <ais-highlight :hit="item" attribute="name" v-if="item.name" />
          <strong>$ {{ item.price }}</strong> <img :src="item.image" />
        </div>
      </ais-hits>
      <ais-pagination />
    </ais-instant-search>
  </div>
</template>

That’s it! You can find the complete example on GitHub.

Did you find this page helpful?