Install
npm install vue-instantsearch algoliasearch instantsearch.css
Setup
Edit nuxt.config.js
export default { // ... /* ** for Algolia InstantSearch nested query parameter */ router: { parseQuery(query) { const qs = require("qs") /* return qs.parse(query, { arrayLimit: 100, ignoreQueryPrefix: true }); */ return qs.parse(query) }, stringifyQuery(query) { const qs = require("qs") const result = qs.stringify(query) return result ? `?${result}` : '' } }, /* ** Build configuration */ build: { transpile: ['vue-instantsearch', 'instantsearch.js/es'], /* ** You can extend webpack config here */ /* extend (config, ctx) { } */ } // ...}
Usage
Create pages/search.vue
. Replace
index-name
(UnderIndices
in Algolia Dashboard)Application ID
(UnderAPI Keys
in Algolia Dashboard)Search-Only API Key
A few changes are made
- Prevent search/listing if there if no query
- Show result in row instead of grid
- Hide pagination if only 1 page of result
- Support Routing Url
Routing Url
- By default, routing url use the format of
/search?INDEX_NAME%5Bquery%5D=Hello
({INDEX_NAME: {query: "Hello"}}
) writeState
andreadState
is implemented to support/search?query=Hello
instead. (NOTE: this might not work if multiple index is used)
<template> <ais-instant-search-ssr> <ais-search-box /> <client-only> <ais-powered-by /> </client-only> <ais-hits> <div slot="item" slot-scope="{ item }"> <!-- show name --> <h2><ais-highlight attribute="name" :hit="item"/></h2> <!-- show content --> <div>{{ item.content }}</div> <!-- show content with highlight --> <div><ais-highlight attribute="content" :hit="item"/></div> <!-- show content with snippet: need to setup Snipetting in Indices --> <div><ais-snippet attribute="content" :hit="item"/></div> </div> </ais-hits> <ais-state-results> <template slot-scope="{ state: { query }, results: { hits, nbPages } }"> <div v-if="query && hits.length == 0">No results</div> <div v-else></div> <ais-pagination v-if="nbPages > 0"/> </template> </ais-state-results> </ais-instant-search-ssr></template><script>import { AisInstantSearchSsr, // AisRefinementList, AisHits, AisHighlight, AisSearchBox, // AisStats, AisPagination, AisSnippet, AisStateResults, AisPoweredBy, createServerRootMixin,} from 'vue-instantsearch'import algoliasearch from 'algoliasearch/lite'import 'instantsearch.css/themes/algolia-min.css'const indexName = 'live'const algoliaClient = algoliasearch( '3P********', // Application ID '0a5f4c5c0b18********************' // Search-Only API Key);// to disable listing on loadconst searchClient = { search(requests) { if (requests.every(({ params }) => !params.query)) { return Promise.resolve({ results: requests.map(() => ({ hits: [], nbHits: 0, nbPages: 0, processingTimeMS: 0, })), }); } return algoliaClient.search(requests); },};// remove indexNamefunction writeState(routeState) { if (indexName in routeState) routeState = routeState[indexName] return routeState}// restore indexNamefunction readState(routeState) { routeState = { [indexName]: routeState } return routeState}// read and write router state to support algolia query parameterfunction nuxtRouter(vueRouter) { return { read() { return readState(vueRouter.currentRoute.query); }, write(routeState) { routeState = writeState(routeState) vueRouter.push({ query: routeState, }); }, createURL(routeState) { routeState = writeState(routeState) const url = vueRouter.resolve({ query: routeState, }).href; return url }, onUpdate(cb) { if (typeof window === 'undefined') return; this._onPopState = event => { const routeState = event.state; // On initial load, the state is read from the URL without // update. Therefore, the state object isn't present. In this // case, we fallback and read the URL. if (!routeState) { cb(this.read()); } else { cb(routeState); } }; window.addEventListener('popstate', this._onPopState); }, dispose() { if (typeof window === 'undefined') return; window.removeEventListener('popstate', this._onPopState); }, };}export default { serverPrefetch() { return this.instantsearch.findResultsState(this).then(algoliaState => { this.$ssrContext.nuxt.algoliaState = algoliaState; }); }, beforeMount() { const results = this.$nuxt.context.nuxtState.algoliaState || window.__NUXT__.algoliaState; this.instantsearch.hydrate(results); }, data() { const mixin = createServerRootMixin({ searchClient, indexName, routing: { router: nuxtRouter(this.$nuxt.$router), } }) return { ...mixin.data(), }; }, provide() { return { // Provide the InstantSearch instance for SSR $_ais_ssrInstantSearchInstance: this.instantsearch, }; }, components: { AisInstantSearchSsr, // AisRefinementList, AisHits, AisHighlight, AisSearchBox, // AisStats, AisPagination, AisSnippet, AisStateResults, AisPoweredBy, }, /* head() { return { link: [ { rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/npm/[email protected]/themes/algolia-min.css', }, ], }; }, */};</script><style>.ais-SearchBox { margin-bottom: 1em;}.ais-Hits-item { width: 100%; border: none; box-shadow: none;}.ais-Highlight { font-size: inherit;}.ais-Highlight-highlighted { font-size: inherit;}</style>
References: