I am developing an algolia search solution for an ecommerce app with around ~30000 products.
I tried to use the <ais-configure/> component to filter my results before showing them on the page, as I am working with different pages for each category of products and some facet filters also need to be applied if a user was to click on a promotion which would lead to this search result in the code below.
The problem is:
Whenever I use the filters in <ais-configure/> the same filters are not picked up by my <ais-refinement-list/> component which should have the refined facets highlighted.
The below code is not working but descriptive enough for the problem.
Template
<ais-instant-search :search-client.camel="searchClient" index-name="products">
<ais-configure
:filters.camel="searchParameters.filters"
:facet-filters.camel="searchParameters.facetFilters"
/>
<ais-infinite-hits>
<div
slot="loadMore"
slot-scope="{ isLastPage, refineNext }"
class="w-full my-12 px-6 text-sm-black-primary"
>
<button
class="
w-full
h-20
border-4
rounded-md
border-sm-black-primary
font-semibold
text-xl
mb-24
"
:disabled="isLastPage"
#click="refineNext"
>
See more
</button>
</div>
</ais-infinite-hits>
<products-filter-section>
<div v-for="(filterType, id) in filterTypes" :key="id">
<ais-refinement-list :attribute="filterType" searchable show-more>
<template
slot-scope="{
items,
isShowingMore,
isFromSearch,
canToggleShowMore,
refine,
createURL,
toggleShowMore,
searchForItems,
}"
>
<div>
<UtilsFancyTitle
:text="filterType"
text-size="text-lg"
class="ml-4 my-4"
/>
<input
class="bg-white"
#input="searchForItems($event.currentTarget.value)"
/>
<ul class="flex flex-wrap">
<li v-if="isFromSearch && !items.length">No results ...</li>
<li v-for="item in items" :key="item.value" class="my-4 mr-4">
<a
:href="createURL(item)"
:class="
item.isRefined
? 'font-semibold bg-sm-yellow-primary border-sm-yellow-primary '
: 'font-medium'
"
class="
border-4 border-sm-black-primary
py-1.5
px-2
rounded-md
text-sm
"
#click.prevent="refine(item.value)"
>
<ais-highlight attribute="item" :hit="item" />
( {{ item.count.toLocaleString() }} )
</a>
</li>
</ul>
<div class="flex justify-center items-center">
<div class="w-1/4 h-1 bg-sm-black-primary rounded-md"></div>
<button
class="w-2/4 font-semibold my-6"
:disabled="!canToggleShowMore"
#click="toggleShowMore"
>
{{ !isShowingMore ? 'Show more' : 'Hide' }}
</button>
<div class="w-1/4 h-1 bg-sm-black-primary rounded-md"></div>
</div>
</div>
</template>
</ais-refinement-list>
</div>
</products-filter-section>
</ais-instant-search>
Script
import {
AisInstantSearch,
AisRefinementList,
AisInfiniteHits,
AisConfigure,
} from 'vue-instantsearch'
import algoliasearch from 'algoliasearch/lite'
import { mapGetters } from 'vuex'
export default {
components: {
AisInstantSearch,
AisRefinementList,
AisInfiniteHits,
AisConfigure,
},
data() {
return {
searchClient: algoliasearch(
'[API-ENDPOINT]',
'[API-KEY]'
),
filterTypes: [
'type',
'color',
],
searchParameters: {
filters: `category:${this.$route.path.split('/').pop()}`,
facetFilters: `type:someProductType`,
},
}
},
head() {
return {
link: [
{
rel: 'stylesheet',
href: 'https://cdn.jsdelivr.net/npm/instantsearch.css#7.4.5/themes/reset-min.css',
},
],
}
},
}
Is there any working solution for this?
The facets should be auto selected and in their refined state (for example: having their isRefined property set to rue) whenever the algolia query loads and the page renders.
This question has been posted a while ago, so you might already have found an answer, but I'll post mine anyway since I landed here after having the same issue.
In my case, at least, there were 2 problems:
I hadn't configured the "Attributes for faceting". This is explained in the docs and can be done easily via the admin or via API.
I was specifying the facets incorrectly in code. In your example above I think you should have:
searchParameters: {
facetFilters: [[`type:someProductType`]],
},
Note that now facetFilters is an array within an array.
What helps me when I'm unsure I open the admin UI of Algolia, select a search and apply whichever filter I want and then check the raw output. An example from my admin ui:
This gives me an idea of the correct shapes InstantSearch needs.
Related
I need to enable/disable specific inputs on button click.
My problem here is when I click my button, ALL the inputs enable/disable I am having a hard time targeting a single one.
I use props “articles” which returns the “id”, “title” and “body” of my articles.
<div v-for="(article, index) in articles" v-bind:key="article.id">
<div class="flex">
<div class="flex-1 px-2 py-2">
<input
v-model="article.title"
type="text"
:disabled="isDiabled"
class="w-full disabled:opacity-75 bg-gray-300 focus:bg-white"
/>
</div>
<div class="flex-1 px-2 py-2">
<input
v-model="article.body"
type="text"
:disabled="isDiabled"
class="w-full disabled:opacity-75 bg-gray-300 focus:bg-white"
/>
</div>
<div class="px-2 py-2">
<button
class="px-2 py-2 border-gray-400 bg-gray-800 rounded text-white hover:border-gray-300 items-end"
#click="enableEdit"
>
Edit
</button>
</div>
</div>
</div>
<script>
export default {
props: ["articles"],
data() {
return {
isDiabled: true,
};
},
methods: {
enableEdit() {
this.isDiabled = false;
},
},
};
</script>
Every article has his own button and I want only the two inputs of the button that is clicked to be enabled/disabled and not all of them.
As being pointed you need to add also that property to your articles... of course you don't need to do this in the database, still using it as a property you need also to emit this change of the flag to the parent.
You could also use a local version of the articles where you add also this isDisabled property, you fill this new variable at the creation of the component based of course on the property you received from the parent,
created(){
this.local_articles = this.articles.map(e => {
return {...e, isDisabled: true} }
)
},
this way you don't need to propagate nothing to the parent as you can handle all internally.
This fiddle gives a solution to your problem.
Make a boolean property for each article, then change the binding of disabled to that property.
I am attempting to iterate over a simple array using the v-for directive in my Nuxt.js App. Please see below.
<template>
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<nuxt-link class="navbar-brand" to="/">
<img class="image nav-logo" :src="logoSrc" alt="Logo" />
</nuxt-link>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav" >
<li :v-for="link in links" :key="link.label">
{{ link.label }}
</li>
</ul>
</div>
</nav>
</template>
<script>
const linkArray = [
{
label: "Home",
href: "/",
class: ""
},
{
label: "ABout",
href: "/",
class: ""
},
{
label: "Our Menu",
href: "/",
class: ""
},
{
label: "Contact Us",
href: "/",
class: "db-outline-cta"
}
]
export default {
name: "Nav",
data() {
return {
logoSrc: '/img/davidsbarlogo.png',
links: linkArray
}
}
}
</script>
As you can see, this is my component. I am going to be dynamically getting data for this component inside asyncData() later on when my cms is wired up, but I wanted to have some placeholder content.
I am repeatedly getting this error:
ERROR [Vue warn]: Error in render: "TypeError: Cannot read property 'label' of undefined" 00:00:49
I have tried with and wihtout the :key property, I know I should include one. I am fairly new to vue, if anyone has a recommendation I would be most grateful.
<ul class="navbar-nav">
<li v-for="link in links" :key="link.label">{{link.label}}</li>
</ul>
Works like a charm
https://codesandbox.io/s/recursing-kapitsa-rnr4h
Try this
<li v-for="(link, index) in links" :key="index">
{{ link.label }}
</li>
However, using index as key isn't the best practice (as explained in this post) but since there's no truly unique id in your link object, this will do.
I have a (strange) problem that I can't figure out.
I created a home page with some data from a call with the Apollo module. That all works fine, but when I try to navigate to the next page I get the error 'Cannot read property '$apolloData' of undefined'. The problem is that on the second page I'm navigating to there is no call to Apollo. It's just a static page.
Does Apollo keep refetching the data even when you're not on the page where the queries are called?
Is there something I should do before going to the next page?
I have no idea what the problem could be so any help would be greatly appreciated!
Queries that are being called one the homepage:
apollo: {
query1: {
query: query1,
prefetch: true,
},
query2: {
query: query2,
prefetch: true,
variables () {
return { id: this.query2variable }
}
},
query3: {
query: query3,
variables () {
return { id: this.query3variable }
},
// Disable the query
skip () {
return this.skipQuery
},
}
},
Navigation to the second page:
<div
class="text-titleColor font-bold w-full block flex-grow lg:flex lg:items-center lg:w-auto"
:class="open ? 'block' : 'hidden'"
>
<div class="text-sm lg:flex-grow text-right">
<router-link
to="/"
class="block mt-4 mr-4 p-1 lg:inline-block lg:mt-0 border-b-2 border-solid border-transparent hover:border-titleColor"
>Search </router-link>
<router-link
to="aboutus"
class="block mt-4 mr-4 p-1 lg:inline-block lg:mt-0 border-b-2 border-solid border-transparent hover:border-titleColor"
>About us</router-link>
<router-link
to="/"
class="block mt-4 mr-4 p-1 lg:inline-block lg:mt-0 border-b-2 border-solid border-transparent hover:border-titleColor"
>Login </router-link>
<router-link
to="/"
class="inline-block text-sm px-4 py-2 text-hover bg-titleColor leading-none rounded border-transparent mt-4 lg:mt-0"
>Account aanmaken</router-link>
</div>
</div>
When I go back to the homepage coming from the second page, it works again on the home page.
Make sure you're returning an object on data() on page you're getting the error.
data(){
return{
// your data
}
}
I was getting same error and took me some time until I realized I was missing the return {} wrapping my data. It was such obvious and basic that I wasn't looking there.
I want to implement a pagination component b-pagination w/bootstrap-vue but it will only display page one. I am following the setup in documentation but they only show an example using a table not an unordered list. I have :
<template>
<div class="results overflow-auto" v-cloak>
<h3>Search Results</h3>
<modal v-if="showModal" #close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<template v-slot:header>
<h1>NASA Image</h1>
</template>
<template v-slot:body>
<b-img class="modal-image" v-bind:src="attribute"></b-img>
</template>
</modal>
<!-- ======== Pagination Markup ============ -->
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
:items="items"
aria-controls="my-list"
></b-pagination>
<p class="mt-3">Current Page: {{ currentPage }}</p>
<!-- ==========End Pagination Markup ======== -->
<!-- Limit output to 100 items -->
<ul
class="search-results"
id="my-list"
>
<li v-for="(item, index) in propsResults.items.slice(0,100)" :key="index">
{{ item.data[0].title}}
<span>
<b-img
thumbnail
class="thumbnail"
:src="item.links[0].href"
alt="Fluid image"
id="show-modal"
v-on:click="imageModal"
></b-img>
</span>
</li>
</ul>
</div>
</template>
and my javascript is :
export default {
name: "SearchResults",
props: ["propsResults"],
data() {
return {
showModal: false,
attribute: "",
perPage: 10,
currentPage: 1,
items: this.$props.propsResults.items
};
},
computed: {
rows() {
return this.$props.propsResults.items.length;
}
}
The pagination component is displaying all 100 items of items array on one page. I should also note I do not see the items array in the b-pagination props object per Vue dev tools in FF. is this normal? Any insight appreciated..
You should still be using currentPage to choose which items to show. The b-pagination component only changes that number.
Try using this line:
<li v-for="(item, index) in items.slice(10*(currentPage-1),10*(currentPage))" :key="index">
I'm new in VueJS and I'm try to do something like this
I have 3 components, siderbar ; siderbar-item and sidebar-drop
siderbar.vue is a wrapper when you can put siderbar-item and sidebar-drop, and inside sidebar-drop you can put sidebar-item too
for example:
<sidebar>
<sidebar-item url="myurl" title="Dashboard" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
<sidebar-drop title="News" icon="fas fa-tachometer-alt">
<sidebar-item url="myurl_news" title="News list" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
<sidebar-item url="myurl_news_add" title="Add News" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
</sidebar-drop>
<sidebar-drop title="Categories" icon="fas fa-tachometer-alt">
<sidebar-item url="myurl_categories" title="Categories List" icon="fas fa-tachometer-alt" badge=""></sidebar-item>
</sidebar-drop>
</sidebar>
sidebar.vue is very simple:
<aside>
<slot></slot>
</aside>
sidebar-item.vue
<template>
<ul class="menu-list" >
<li>
<a :href="url" :class="{'is-active': is_active }">
<span class="icon"><i v-if="icon" :class="icon"></i></span>
{{ title }}
<span v-if="badge" class="button badge is-info is-small is-pulled-right "> {{ badge }}</span>
</a>
</li>
</ul>
</template>
<script>
export default {
props: ['title', 'url', 'icon', 'badge'],
data() {
return {
is_active: this.$current_url === this.url
}
}
}
</script>
and sidebar-drop.vue
<template>
<ul class="menu-list" v-if="title">
<li #click="openMenu" :class="{'is-open': is_open}">
<a class="">
<span v-if="icon" class="icon"><i class="fas fa-tachometer-alt"></i></span>
{{ title }}
<span class="badge is-pulled-right" style=""> <i class="fas fa-chevron-left"></i> </span>
</a>
<slot></slot>
</li>
</ul>
</template>
<script>
export default {
props: ['title', 'icon'],
data() {
return {
is_open: false,
}
},
methods: {
openMenu(){
this.is_open = !this.is_open;
},
}
}
</script>
It works great, but I have a little problem. If you notice, sidebar-item has a data called "is_active", it only put a CSS class that this item is active. Great.
Now I'm trying to get into sidebar-drop if a least one of his children has data "is-active" in true, change the is_open sidebar-drop parent data to true.
Example: I click "News" item.
Ul li News is active... works!
Ul li parent drop is open... don't work
Is possible make that?
Thanks everyone!
Update:
I put it in Fiddler to more clear:
https://jsfiddle.net/h35qy2zs/
I do in sidebar-item a random is_active.
If any child of sidebar-drop (sidebar-item's) data is_active:true, sidebar-drop data is_open is true too.
So, it show in color yellow and open
Result:
But now, both are closed at refresh page.