Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Simple Developer
Simple Developer
You can provides powerful search feature in your websiste using Algolia.
You can provides powerful search feature in your websiste using Algolia.
We need install total 5 package to finish this task.
npm install vue-instantsearch algoliasearch nuxt-content-algolia remove-markdown v-click-outside --save
// /plugins/vue-instantsearch.js import Vue from 'vue' import InstantSearch from 'vue-instantsearch' Vue.use(InstantSearch)
// /nuxt.config.js export default { // ... plugins: [ '~/plugins/vue-instantsearch' ], // ... }
// /nuxt.config.js export default { build: { transpile: ['vue-instantsearch', 'instantsearch.js/es'] }, }
nuxt-content-algolia
to send index to Algolia// /nuxt.config.js export default { // ... buildModules: [ 'nuxt-content-algolia' ], nuxtContentAlgolia: { appId: process.env.ALGOLIA_APP_ID, apiKey: process.env.ALGOLIA_API_KEY, paths: [ { name: 'articles', index: 'articles', fields: ['title', 'description', 'bodyPlainText'] } ] }, }
.env
file and set required variablesAlgolia API Key is is not your search only key but the key that grants access to modify the index.
You can generate a new API key in Algolia admin page.
// /.env ALGOLIA_APP_ID=your-algolia-app-id ALGOLIA_API_KEY=your-algolia-api-key
Html tags and other attributes is not needed when search. So, we create a plain body text using remove-markdown package.
// /nuxt.config/js export default { // ... hooks: { 'content:file:beforeInsert': (content) => { const removeMd = require('remove-markdown'); if (content.extension == 'md') { content.bodyPlainText = removeMd(content.text); } } } }
Search.vue
component in /components folder<template> <ais-instant-search :search-client="searchClient" index-name="articles" > <ais-configure :attributesToSnippet="['bodyPlainText']" :hits-per-page.camel="5" /> <ais-autocomplete v-click-outside="onClickOutside"> <div slot-scope="{ currentRefinement, indices, refine }" class="md:relative"> <div class="relative"> <font-awesome-icon :icon="['fas', 'search']" class="absolute h-4 text-gray-400 mt-3 ml-3" /> <input type="search" ref="searchInput" class="w-full py-2 px-4 pl-10 bg-gray-100 rounded" :value="currentRefinement" @input="refine($event.currentTarget.value)" placeholder="Search - Ctrl+K to focus" autocomplete="off" @focus="showResults = true" @keydown.up.prevent="highlightPrevious(indices[0].hits.length)" @keydown.down.prevent="highlightNext(indices[0].hits.length)" @keydown.enter="goToArticle(indices)" > </div> <div v-if="currentRefinement.length && showResults" class="absolute right-0 z-10 transform mt-3 px-2 w-screen max-w-md sm:px-0"> <div class="rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 overflow-hidden"> <div class="relative grid gap-6 bg-white text-gray-700 px-4 py-4 sm:gap-8 sm:p-4"> <div v-if="currentRefinement" v-for="section in indices" :key="section.objectID" class="divide-y divide-gray-300"> <div v-if="section.hits.length"> <h2 class="uppercase text-gray-700 py-1 px-2">{{ section.indexName }}</h2> </div> <NuxtLink to="#" v-for="(hit, index) in section.hits" :key="hit.objectID" class="block text-sm col-span-2 py-2 transition ease-in-out duration-150" :class="{ 'bg-gray-100': isCurrentIndex(index) }"> <div class="px-2" @mouseover="highlightedIndex = index"> <ais-highlight attribute="title" :hit="hit" class="block text-gray-600 font-semibold tracking-wide" /> <ais-snippet attribute="bodyPlainText" :hit="hit" class="block text-gray-400 font-base" /> </div> </NuxtLink> </div> <ais-powered-by theme="light" class="px-2" /> </div> </div> </div> </div> </ais-autocomplete> </ais-instant-search> </template> <script> import algoliasearch from 'algoliasearch/lite' import vClickOutside from 'v-click-outside' export default { directives: { clickOutside: vClickOutside.directive }, data() { return { searchClient: algoliasearch ('6C3W4JP2I6', 'ea72adbc6e9f7b4da0b111f7319cd3a3'), showResults: false, highlightedIndex: -1 } }, mounted() { this.$nextTick(function () { window.addEventListener('keydown', event => { if((event.metaKey || event.ctrlKey) && event.key === 'k') { this.$refs.searchInput.focus() event.preventDefault() } }) }) }, watch: { '$route' () { this.showResults = false this.$refs.searchInput.blur() } }, methods: { onClickOutside() { this.showResults = false }, highlightPrevious(resultsCount) { if (this.highlightedIndex > 0) { this.highlightedIndex -= 1 } else { this.highlightedIndex = resultsCount - 1 } }, highlightNext(resultsCount) { if (this.highlightedIndex < resultsCount - 1) { this.highlightedIndex += 1 } else { this.highlightedIndex = 0 } }, isCurrentIndex(index) { return index === this.highlightedIndex }, goToArticle(indices) { this.$nuxt.$router.push('/articles/' + indices[0].hits[this.highlightedIndex].objectID) } } } </script>
<Search />
vue component in your layout of page<template> <header> <Search /> </header> </template>