I have below navbar in Vue. When I pass an array to it it doesn't show the menu.
<template>
<div>
<div class="mt-5 px-2">
<li v-for="item in items" :key="item.name">
<a
href="item.link"
class="group flex items-center px-2 py-2 text-base leading-6 font-medium rounded-md text-white bg-gray-900 focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150"
>
<svg
class="mr-4 h-6 w-6 text-gray-300 group-hover:text-gray-300 group-focus:text-gray-300 transition ease-in-out duration-150"
stroke="currentColor"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l9-9 9 9M5 10v10a1 1 0 001 1h3a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1h3a1 1 0 001-1V10M9 21h6"
/>
</svg>
{{ item.name }}
</a>
</li>
</div>
</div>
<!--
items: [
{ name: 'Foo' },
{ link: 'Bar' }
]
-->
</template>
<script>
export default {
name: 'Nav',
props: ['items'],
data() {
return {
parents: [],
};
},
};
</script>
<style scoped></style>
Your items-array you show as example data is unsuitable for the template:
the items-array contains two objects, one with a single property name, the other with a single property link. But based on the template it looks as if name and link should be part of the same object, e.g.:
items: [
{ name: 'Foo1', 'link': 'Bar1' },
{ name: 'Foo2', 'link': 'Bar2' }
]
With the original array passed as by your example, there will likely be an error shown in the browser's console due to item.link not being defined for the first object, and thus probably stopping the rendering process of Vue.
The main problem in your example is that you named your component Nav, but HTML5 already has this element which will cause a collision between your component and HTML's element, just rename into something else.
After you change the name make sure that the array that you pass to that component looks like this - [{name: 'a', link: 'http://a.com'}, {name: 'b', link: 'http://b.com'}].
Related
I have a view in Vue project.
Home.vue
<template>
<TestLayout >
<Card/>
<Card/>
<Card/>
</TestLayout>
</template>
<script>
import TestLayout from "../components/TestLayout.vue"
import Card from "../components/Card.vue"
export default {
name: "Home",
props:{
isList:{
type: Boolean
}
},
components: {
TestLayout,
Card
},
}
</script>
The TestLayout has a section where we can display cards in list or grid view
TestLayout.vue
<template>
<div class="flex border-solid ">
<ListBulletIcon class="h-10 w-10 cursor-pointer shadow border-2 border-indigo-600 rounded-l p-2"
#click="listView = true" />
<TableCellsIcon #click="listView = false"
class="h-10 w-10 cursor-pointer shadow border-2 border-indigo-600 rounded-r p-2" />
</div>
<section
:class="[listView ? 'md:grid-cols-1 grid-cols-1' : 'md:grid-cols-4 grid-cols-2', 'rounded-md grid gap-5 col-span-full']">
<slot :listView="listView"></slot>
</section>
</template>
<script>
import {
ListBulletIcon,TableCellsIcon} from '#heroicons/vue/24/outline'
export default {
data: function () {
return {
listView: false,
}
},
components: {
ListBulletIcon,
TableCellsIcon,
},
}
}
</script>
I want to change the style of Card.vue based on whether user clicks grid view or list view icon.
For example, I want to add this style to Card.vue div tag in its template:
:class="[isList ? 'dark:bg-midnight' : 'dark:bg-red-300', 'min-h-80 w-full bg-gray-50 shadow-xl rounded-md flex flex-col']"
How will I check isList is clicked or not?
How can I achieve this?
You're half way there. After defining a slot prop (<slot :listView="listView"></slot>) you should access it in parent and pass it down to slot components.
<TestLayout>
<template v-slot="{ listView }">
<Card :isList="listView" />
<Card :isList="listView" />
</template>
</TestLayout>
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.
I'm trying to change the active tab of a bootstrap-vue b-tabs when the tab title is hovered over, not only when clicked on. I'm having trouble isolating this event.
In the following Codepen example, I can isolate the event when the image is being hovered over, however I want to isolate the event when the title ('Tubes and Vials' for example) is being hovered over.
I'm fairly new to Vue so I apologize if this is a simple answer, but I haven't struggled with this for a while now and haven't been able to figure this out. Thanks!
Component File
<template>
<b-container class="px-3" fluid>
<div>
<h3>Range Of Glass Products We Inspect</h3>
<p>Anything formed from tubular glass</p>
</div>
<div>
<b-tabs content-class="mt-3" align="left" class="vial-types" vertical>
<b-tab
v-for="glassItem in productRange"
v-bind:key="glassItem.type"
v-bind:ref="glassItem"
v-bind:title="glassItem.type"
#mouseover.native="greet()"
#mouseleave.native="greet()"
>
<b-img
v-bind:src="glassItem.image"
alt="Factory Image">
</b-img>
</b-tab>
</b-tabs>
</div>
</b-container>
</template>
<script>
export default {
name: "ProductRange",
data() {
return {
productRange: [
{type: "Screw & Flanged Head", image:"https://picsum.photos/600/400/", hover: false},
{type: "Tubes and Vials", image:"https://picsum.photos/600/400/", hover: false},
{type: "Pipettes, Syringes, Filling Needles", image:"https://picsum.photos/400/400/",hover: false},
{type: "Ampoules", image:"https://picsum.photos/600/400/", hover: false},
{type: "Custom Geometries Per Customer Specification", image:"https://picsum.photos/600/400/", hover: false}
]
}
},
methods: {
greet: function () {
console.log("Hovering");
}
}
}
</script>
<style lang="sass">
</style>
You could also use the b-tab's title slot, and then add a hover/unhover listener in there:
<b-tabs content-class="mt-3" align="left" class="vial-types" vertical>
<b-tab
v-for="glassItem in productRange"
v-bind:key="glassItem.type"
v-bind:ref="glassItem"
>
<template v-slot:title>
<div
#mouseover="hovered"
#mouseleave="unHovered"
>
{{ glassItem.type }}
</div>
</template>
<b-img
v-bind:src="glassItem.image"
alt="Factory Image">
</b-img>
</b-tab>
</b-tabs>
Sadly I don't think there's a built-in way to easily do this.
However, you can still solve this by hiding the standard tabs and instead reconstruct the structure yourself using b-nav and binding to the b-tabs v-model.
You can then add your events the b-nav-item as they'll be working as your tabs.
new Vue({
el: "#app",
data: {
selectedTab: 0,
productRange: [
{
type: "Screw & Flanged Head",
image: "https://picsum.photos/600/400/"
},
{
type: "Tubes and Vials",
mage: "https://picsum.photos/640/400/"
},
{
type: "Pipettes, Syringes, Filling Needles",
image: "https://picsum.photos/400/400/"
},
{
type: "Ampoules",
image: "https://picsum.photos/600/400/"
},
{
type: "Custom Geometries Per Customer Specification",
image: "https://picsum.photos/700/400/"
}
]
},
methods: {
greet: function() {
console.log("hovering");
},
onTabHover(glassItem) {
console.log("Tab hovered", glassItem)
}
}
});
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.4.2/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue#2.6.10/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.4.2/dist/bootstrap-vue.min.js"></script>
<b-container id="app" class="px-3"fluid>
<div>
<h3>Range Of Glass Products We Inspect</h3>
<p>Anything formed from tubular glass</p>
</div>
<div>
<b-row>
<b-col cols="auto">
<b-nav pills vertical>
<b-nav-item v-for="(glassItem, index) in productRange"
:active="selectedTab === index"
#click="selectedTab = index"
#mouseenter="onTabHover(glassItem)">
{{ glassItem.type }}
</b-nav-item>
</b-nav>
</b-col>
<b-col>
<b-tabs v-model="selectedTab"
content-class="mt-3"
class="vial-types"
nav-class="d-none">
<b-tab
v-for="glassItem in productRange"
:key="glassItem.type"
:ref="glassItem"
:title="glassItem.type"
#mouseover.native="greet()"
#mouseleave.native="greet()"
>
<b-img
:src="glassItem.image"
alt="Factory Image">
</b-img>
</b-tab>
</b-tabs>
</b-col>
</b-row>
</div>
</b-container>
I'm creating a custom select component on Vue and I'm using tailwind to style it.
I would like to have an chevron-down caret svg align to the right, which will open the select options when clicked. I'm having some trouble with this.
<template>
<div class="flex flex-wrap mb-4 relative">
<select :value="value" #input="$emit('input', $event.target.value)" class="w-full h-12 pl-4 bg-white focus:bg-grey-10 focus:text-grey-5 border-2 border-grey-9 rounded-lg text-sm focus:outline-none">
<option :value="null">
Select an option
</option>
<option v-for="option in options" :value="option.slug">{{ option.name }}</option>
</select>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" class="absolute mt-4 mr-4 right-0 cursor-pointer">
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/>
</svg>
</div>
</template>
<script>
export default {
props: [ 'value'],
data () {
return {
options: [
{ slug: 'test1', name: 'Test 1' },
{ slug: 'test2', name: 'Test 2' },
],
};
},
}
</script>
This is what it looks like, however the svg when clicked does not open the dropdown.
Any suggestions?
To the <svg> tag, you need to add the .pointer-events-none class. From the Tailwind Docs
Use .pointer-events-none to make an element ignore pointer events. The pointer events will still trigger on child elements and pass-through to elements that are "beneath" the target.
There is a helpful custom select example in the Tailwind docs.
I currently have a vue component that I use to select drinks to be ordered.
You can increment or decrement the amount of drinks per type.
There are 2 buttons (Increment and decrement), the decrement button needs to be hidden when the amount of ordered drinks for that type is equal to 0. This can easily be accomplished by using :class="drink.amount > 0 ? 'visible':'invisible'" using the tailwind classes which sets visibility:hidden css property. When using this method it looks like:
Now I tried to implement css animations (sliding the button underneath the drink block to hide it and later hide from dom). I want to implement this using the vue transition component since this is heavily used throughout the application.
Now I have the problem that the vue transition component only works with a limited amount of vue functions among which v-if and v-show. v-if removes the html from the dom. v-show sets the property display:none both of these functions have the effect of shifting the buttons:
I would like to known how I can use the vue transition component and get the requested aligned buttons as I got without the animation.
<div v-for="drink in drinks" class="w-full flex">
<div class="mx-auto flex">
<transition name="transition-slide-right">
<div class="bg-white w-16 h-16 flex justify-center items-center mt-12"
v-show="drink.amount">
<p>-</p>
</div>
</transition>
<div class="bg-brand w-32 h-24 flex justify-center items-center my-8 z-30">
<p>{{drink.name}} ({{drink.amount}})</p>
</div>
<div class="bg-white w-16 h-16 flex justify-center items-center mt-12">
<p>+</p>
</div>
</div>
</div>
And accompanying script.
<script>
export default {
name: "CreateTransaction",
data: function() {
return {
drinks: [
{name: 'Cola', price: '1.0', amount: 1},
{name: 'Sinas', price: '1.0', amount: 0}
],
}
}
}
</script>
Finally the css:
.transition-slide-right-enter-active, .transition-slide-right-leave-active {
transition: transform .5s;
}
.transition-slide-right-enter, .transition-slide-right-leave-to {
transform: translateX(100%);
}
As a current workaround I removed the transition component, added the transition-slide-right-enter-active class and if the count == 0 I conditionally add the transition-slide-right-enter class. This does not hide the element but hiding it is not required since I is moved underneath the center block.
you can try the following code
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div class="row" id="todo-list-example">
<div v-for="(drink,index) in drinks" class="w-full flex">
<transition name="transition-slide-right"><button v-show="drink.amount" type="button" class="btn btn-danger" v-on:click="click1(index)">-</button></transition>
<a class="btn btn-success" >{{drink.name}} ({{drink.amount}})</a>
<button type="button" class="btn btn-danger" v-on:click="click2(index)">+</button>
</div>
</div>
<style>
.w-full{margin:10px 0px;}
.transition-slide-right-enter-active, .transition-slide-right-leave-active {
transition: transform .5s;
}
.transition-slide-right-enter, .transition-slide-right-leave-to {
transform: translateX(100%);
}
</style>
<script>
new Vue({
el: '#todo-list-example',
data(){
return {
drinks: [
{name: 'Cola', price: '1.0', amount: 1},
{name: 'Sinas', price: '1.0', amount: 1}
],
}
},
methods:{
click1:function(index){
this.drinks[index].amount=this.drinks[index].amount-1;
},
click2:function(index){
this.drinks[index].amount=this.drinks[index].amount+1;
}
}
})
</script>