Setup Algolia InstantSearch on Nuxt (with Query/Routing Url and SSR)
August 19, 2020Perform search via query parameter
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 load
const 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 indexName
function writeState(routeState) {
if (indexName in routeState)
routeState = routeState[indexName]
return routeState
}
// restore indexName
function readState(routeState) {
routeState = {
[indexName]: routeState
}
return routeState
}
// read and write router state to support algolia query parameter
function 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/instantsearch.css@7.3.1/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:
- algo-trading
- algolia
- analytics
- android
- android-ktx
- android-permission
- android-studio
- apps-script
- bash
- binance
- bootstrap
- bootstrapvue
- chartjs
- chrome
- cloud-functions
- coding-interview
- contentresolver
- coroutines
- crashlytics
- crypto
- css
- dagger2
- datastore
- datetime
- docker
- eslint
- firebase
- firebase-auth
- firebase-hosting
- firestore
- firestore-security-rules
- flask
- fontawesome
- fresco
- git
- github
- glide
- godot
- google-app-engine
- google-cloud-storage
- google-colab
- google-drive
- google-maps
- google-places
- google-play
- google-sheets
- gradle
- html
- hugo
- inkscape
- java
- java-time
- javascript
- jetpack-compose
- jetson-nano
- kotlin
- kotlin-serialization
- layout
- lets-encrypt
- lifecycle
- linux
- logging
- lubuntu
- markdown
- mate
- material-design
- matplotlib
- md5
- mongodb
- moshi
- mplfinance
- mysql
- navigation
- nginx
- nodejs
- npm
- nuxtjs
- nvm
- pandas
- payment
- pip
- pwa
- pyenv
- python
- recylerview
- regex
- room
- rxjava
- scoped-storage
- selenium
- social-media
- ssh
- ssl
- static-site-generator
- static-website-hosting
- sublime-text
- ubuntu
- unit-test
- uwsgi
- viewmodel
- viewpager2
- virtualbox
- vue-chartjs
- vue-cli
- vue-router
- vuejs
- vuelidate
- vuepress
- web-development
- web-hosting
- webpack
- windows
- workmanager
- wsl
- yarn