Using VMenu from vuetify with render function (scoped slot) - javascript

I'm trying to use Vuetify's VMenu component and I would like that when a user clicks the button the VMenu shows up. As far as the docs goes it says we should add a scoped slot. Doing with a normal template it works but when I switch to a render function approach it never renders the button.
I have been following the Vue's docs and ended up with:
h(VMenu, { props: { value: isMenuOpen.value } }, [
h(
"template",
{
scopedSlots: {
activator: ({ on, attrs }) => {
debugger; // it never reaches this debugger
return h(VButton, { on, attrs }, 'click me');
}
},
},
[]
),
h(VList, [h(VListItem, [h(VListItemTitle, ["Logout"])])]),
]),
I have tried using a non-arrow function as well:
scopedSlots: { activator: function({ on, attrs }) { return h('div', 'click me'); } }
and return a simple h('div', 'click me') in both non-arrow function and arrow function and nothing seems to work.
How can I pass the scoped slot activator to VMenu component?

Scoped slots are passed through the scopedSlots property of createElement's 2nd argument in the form of { name: props => VNode | Array<VNode> }. In your case, scopedSlots should have two entries: one for activator, and another for default:
import { VMenu, VList, VListItem, VBtn } from 'vuetify/lib'
export default {
render(h) {
return h(VMenu, {
scopedSlots: {
activator: props => h(VBtn, props, 'Open'),
default: () => h(VList, [
h(VListItem, 'item 1'),
h(VListItem, 'item 2'),
h(VListItem, 'item 3'),
]),
},
})
}
}
which is equivalent to this template:
<template>
<v-menu>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on">Open</v-btn>
</template>
<v-list>
<v-list-item>item 1</v-list-item>
<v-list-item>item 2</v-list-item>
<v-list-item>item 3</v-list-item>
</v-list>
</v-menu>
</template>
demo

I wasn't able to fully understand the problem described in my question. This is an answer not to answer the fully original question but to guide future users that may come to this question.
Instead of using a scoped slot I have used the value prop in combination with attach prop. This solution in the end ended up working without no problem.
button(
{
attrs: { "data-account-setting": true },
props: { plain: true, rounded: true, icon: true },
on: { click: onOpenMenuClick },
},
[h(VIcon, ["mdi-account-outline"])]
),
h(
VMenu,
{
props: {
value: isMenuOpen.value,
// waiting on answer on SO
// #see https://stackoverflow.com/questions/67405594/using-vmenu-from-vuetify-with-render-function-scoped-slot
attach: "[data-account-setting]",
minWidth: "300px",
left: true,
offsetY: true,
closeOnContentClick: false,
rounded: true,
},
on: {
input: (value: boolean) => {
isMenuOpen.value = value;
},
},
},
[
h(VList, { props: { dense: true } }, [
h(VListItem, { props: { to: { name: "logout" } } }, [
h(VListItemTitle, { attrs: { 'data-cy-logout': true } }, ["Logout"]),
]),
]),
]
),

Related

How to delay nth child item animation in Vue JS

I want delay animation for another item's directly by Vue Js, not css.
In this moment Vue wait until all items has rendered, and all appear at the same time.
How to do delay by change this.animated = true ?
Item.vue
<template>
<transition name="animation-fade">
<div v-if="this.animated">
{{this.itemProp.content}}
</div>
</transition>
</template>
<script>
export default {
name: 'Item',
props: {
itemProp: Object,
},
data: function() {
return {
animated: false,
loading: true,
};
},
methods: {
delayedShow: function(delay) {
setTimeout(this.toggleItem, delay)
},
},
toggleItem: function() {
this.animated = true;
},
mounted: function() {
this.delayedShow(500);
},
}
</script>
Items are defined in other component and every pass by prop to Item model.
Help me please.

how to alter a single row in v-data-table (not styling, the content)

I wasn't able to find an answer for this use case of v-data-table.I know that you can use template and slots to modify a certain column but what if i want my value to be reflected only in one row? So in my code, everytime a user right clicks on the name column it adds a logo to show the value is copied and then after 3 seconds it removes it from the name -kind of like a toggle effect.
It works well whenever i click on a name on a certain row, and it copies the link value for that specific link by using vue-clipboard's library. However, it also does the same thing for all the other columns that have link. I would like to do it for only one. I couldn't make the vue-clipboard library run in sandbox so i'm sharing my code snippets.
In order to better show the current behavior, this is a screenshot from the v-data-table. (as you can see, it shows the check icon in both rows even though i only click on the first one. The expected behavior would only show the check icon the cell that has been clicked on .
template;
<template>
<v-data-table
:headers="headers"
:items="tableData"
class="display-stats"
:items-per-page="5"
:footer-props="{
'items-per-page-options': rowsPerPageItems,
}"
>
<template v-slot:[`item.name`]="{ item }">
<span v-if="item.link" class="link-span" #contextmenu="copyLink(item.link)">
<a class="preview-link" :href="item.preview" target="_blank">{{ item.name }}</a>
<p v-show="copied">
<v-icon small :color="green">fas fa-check</v-icon>
</p>
</span>
<span v-else>
{{ item.name }}
</span>
</template>
</v-data-table>
</template>
script;
<script lang="ts">
import Vue from 'vue'
import VueClipboard from 'vue-clipboard2'
VueClipboard.config.autoSetContainer = true // add this line
Vue.use(VueClipboard)
interface PriceStats {
rowsPerPageItems: Number[]
copied: boolean
}
export default Vue.extend({
name: 'Component',
props: {
priceData: {
type: Array as () => Array<PriceStats>,
default: () => {},
},
loading: {
type: Boolean,
default: false,
},
},
data(): PriceData {
return {
rowsPerPageItems: [10, 20, 30],
copied: false,
}
},
computed: {
tableData:{
get():PriceStats[]{
if (this.priceData) {
return this.priceData
} else {
return []
}
},
set(newVal:PriceStats){
this.tableData=newVal
}
},
headers(): DataTableHeader[] {
return [
{
text: 'Name',
value: 'name',
},
{
text: 'Age',
value: 'age',
align: 'center',
},
{
text: 'Salary',
value: 'salary',
},
{
text: 'Position',
value: 'format',
},
{
text: 'Date',
value: 'date',
},
{
text: 'Premium',
value: 'premium',
align: 'right',
},
]
},
},
methods: {
copyLink(previewLink: string) {
this.$copyText(previewLink).then(
(e) => {
this.copied = true
setTimeout(()=> {
this.copied = false
},3000)
},
(e) => {
need an error logic here
this.copied = false
}
)
},
},
})
</script>
Let's assume that users cannot have the same name, you can check if the name is equal to the one on the row copied then display the icon there.
like this:
<v-data-table ...>
<span v-if="item.link" class="link-span" #contextmenu="copyLink(item.link,item.name)">
<a class="preview-link" :href="item.preview" target="_blank">{{ item.name }}</a>
<p v-show="item.name == copiedName">
<v-icon small :color="green">fas fa-check</v-icon>
</p>
</span>
</v-data-table>
copiedName can be an external variable that you assign the name of the user using the function copyLink
...
copyLink(previewLink: string,name) {
this.$copyText(previewLink).then(
(e) => {
this.copied = true
this.copiedName = name
setTimeout(()=> {
this.copied = false
},3000)
},
(e) => {
need an error logic here
this.copied = false
}
)
},

creating abstract components that can manage external data

Currently I use Vuetify for base components and would like to create reusable extensions. For example a list containing checkboxes, a datatable column with some functionality etc.
For this question I will take the list containing checkboxes example. I created the following component called CheckboxGroup.vue
<template>
<v-container>
<v-checkbox
v-for="(item, index) in items"
:key="index"
v-model="item.state"
:label="item.title"
></v-checkbox>
</v-container>
</template>
<script>
export default {
props: {
items: Array,
required: true
}
};
</script>
This component takes an array of objects as a property and creates a checkbox for each entry.
Important parts are v-model="item.state" and :label="item.title". Most of the time the state attribute will have a different name, same for the title attribute.
For testing purposes I created a view file called Home.vue holding an array of documents.
<template>
<v-container>
<CheckboxGroup :items="documents"/>
<v-btn #click="saveSettings">Save</v-btn>
</v-container>
</template>
<script>
import CheckboxGroup from "../components/CheckboxGroup";
export default {
components: {
CheckboxGroup
},
data: function() {
return {
documents: [
{
id: 1,
name: "Doc 1",
deleted: false
},
{
id: 2,
name: "Doc 2",
deleted: false
},
{
id: 3,
name: "Doc 3",
deleted: true
}
]
};
},
methods: {
saveSettings: function() {
console.log(this.documents);
}
}
};
</script>
This time title is called name and state is called deleted. Obviously CheckboxGroup is not able to manage the documents because the attribute names are wrong.
How would you solve this problem? Would you create a computed property and rename these attributes? Would be a bad idea I think...
And by the way, is using v-model a good idea? A different solution would be to listen to the changed event of a checkbox and emit an event with the item index. Then you would have to listen for the change in the parent component.
I don't think there is a way to create something like
<CheckboxGroup :items="documents" titleAttribute="name" stateAttribute="deleted"/>
because it would be bad design anyway. I hope that this is a very trivial problem and every Vue developer has been confronted with it, since the primary goal should always be to develop abstract components that can be reused multiple times.
Please keep in mind that this checkbox problem is just an example. A solution for this problem would also solve same or similar problems :)
If I understood what you wanted, it`s not so trivial. Using props is a good idea. You dont need to manage the documents attribute names, just set the attribute name to your component.
Note
Renaming the attributes or using proxies is more resource-intensive like this solution, because you need to run loop to rename the attribute names or apply aliases to data array objects.
Example
CheckboxGroup.vue
<template>
<v-container fluid>
<v-checkbox
v-for="(item, index) in items"
:key="index"
v-model="item[itemModel]"
:label="item[itemValue]"
></v-checkbox>
<hr>
{{items}}
</v-container>
</template>
<script>
export default {
name: "CheckboxGroup",
props: {
items: {
type: Array,
required:true
},
itemValue:{
type:String,
default: 'title',
// validate props if you need
//validator: function (value) {
// return ['title', 'name'].indexOf(value) !== -1
// }
// or make required
},
itemModel:{
type:String,
default: 'state',
// validate props if you need
//validator: function (value) {
// validate props if you need
// return ['state', 'deleted'].indexOf(value) !== -1
// }
// or make required
}
}
};
</script>
Home.vue
<template>
<div id="app">
<checkbox-group :items="documents"
item-value="name"
item-model="deleted"
>
</checkbox-group>
</div>
</template>
<script>
import CheckboxGroup from "./CheckboxGroup.vue";
export default {
name: "App",
components: {
// HelloWorld,
CheckboxGroup
},
data: function() {
return {
documents: [
{
id: 1,
name: "Doc 1",
deleted: false
},
{
id: 2,
name: "Doc 2",
deleted: false
},
{
id: 3,
name: "Doc 3",
deleted: true
}
]
}
}
};
</script>
Based on your example I`v tried to show how to create component to managing object attributes in child component. If you need more information, please let me know.
Some good answers here that definitely solve your issue - you are essentially wanting to pass data down to a child (which isn't bad design - you were on the right track!)..
I am kind of shocked that slots or scoped-slots haven't been mentioned yet... so I figured I would chime in..
Scoped-slots allow you to take advantage of data you are passing to a child - but within the parent. The child essentially "reflects" data back to the parent, which allows you to style the child component/slot however you wish, from the parent.
This is different than just passing data via a prop attribute, because you would have to rely on styling within the child - you couldn't change the styles on a 'per-use' basis. The styles you set in the child would be "hard coded"..
In this example I am riding on top of the already provided label slot that Vuetify provides - just passing my own custom scoped-slot to it.. How to find documentation on v-checkbox slots
I made some minor changes to help spice some things up, and to show how you have greater control over styles this way (and you can use any object prop for the label you want .name, .whatever, .label, etc..)
Lastly, it is important to note that Vuetify already provides a "grouped checkbox" component - v-radio-group - I know it's called "radio-group" but it supports checkboxes...
Edit: fixed the state "issue"...
Scoped Slots With Render Function - Original Answer Moved To Bottom
Thanks to #Estradiaz for collaborating on this with me!
Vue.component('checkboxgroup', {
props: {
items: { type: Array, required: true }
},
render (h) {
return h('v-container', this.items.map((item) => {
return this.$scopedSlots.checkbox({ item });
}));
},
})
new Vue({
el: "#app",
data: {
documents: [{
id: 1,
name: "Doc 1 - delete",
deleted: false,
icon: "anchor",
},
{
id: 12,
title: "Doc 1 - state",
state: false,
icon: "anchor",
},
{
id: 2,
name: "Doc 2 - delete",
deleted: false,
icon: "mouse"
},
{
id: 3,
name: "Doc 3 - delete",
deleted: true,
icon: "watch"
}
]
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet" type="text/css"></link>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://use.fontawesome.com/releases/v5.0.8/css/all.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/MaterialDesign-Webfont/2.1.99/css/materialdesignicons.min.css" rel="stylesheet" />
<div id="app">
<v-app>
<v-container>
<CheckboxGroup :items="documents">
<template #checkbox={item}>
<v-checkbox
v-model="item[(item.name && 'deleted') || (item.title && 'state') ]" color="red">
<template #label>
<v-icon>mdi-{{item.icon}}</v-icon>
{{ item.name || item.title }}
{{ item }}
</template>
</v-checkbox>
</template>
</CheckboxGroup>
</v-container>
</v-app>
</div>
My attempt of a json to component parser
usefull names are welcome
so basically you can target element tagnames as slot #[slotname] or put slot names and target entries to overwrite the default component.
omiting tag property in the component will append children to the parent vnode
Consider:
[
{
ElementTag: 'Liste',
id: 1,
tag: 'p',
items: [
{
ElementTag: 'input',
id: 11,
type: 'checkbox',
title: "Sub Doc 1 - state",
state: true,
slotName: "slotvariant"
},
{
ElementTag: 'input',
id: 12,
type: 'date',
title: "Sub Doc 2 - Date",
date: "",
}
]
},
{
ElementTag: 'input',
id: 2,
type: 'checkbox',
title: "Doc 2 - deleted",
deleted: true,
slotName: 'deleted'
}
]
Example :
Vue.component('Liste', {
props:["tag", "items"],
render(h){
console.log(this.items)
let tag = this.tag || (this.$parent.$vnode && this.$parent.$vnode.tag)
if(tag === undefined) throw Error(`tag property ${tag} is invalid. Scope within valid vnode tag or pass valid component/ html tag as property`)
return h(tag, this.items.map(item => {
const {ElementTag, slotName, ...attrs} = item;
return (
this.$scopedSlots[slotName || ElementTag]
&& this.$scopedSlots[slotName || ElementTag]({item})
)
|| h(ElementTag, {
attrs: attrs,
scopedSlots: this.$scopedSlots
})
}))
}
})
new Vue({
data(){
return {
items: [
{
ElementTag: 'Liste',
id: 1,
tag: 'p',
items: [
{
ElementTag: 'input',
id: 11,
type: 'checkbox',
text: "Sub Doc 1 - state",
state: true,
slotName: "slotvariant"
},
{
ElementTag: 'input',
id: 12,
type: 'date',
title: "Sub Doc 2 - Date",
date: "",
}
]
},
{
ElementTag: 'input',
id: 2,
type: 'checkbox',
title: "Doc 2 - deleted",
deleted: true,
slotName: 'deleted'
}
]}
}
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<Liste tag="p" :items="items">
<template #input="{item}">
<label :for="item.id">
{{ item.title }}
</label>
<input :type="item.type" :id="item.id" v-model="item.date"/>
</template>
<template #slotvariant="{item}">
slotvariant - {{item.text}}<br>
</template>
<template #deleted="{item}">
<label :for="item.id">
{{ item.title }}
</label>
<input :type="item.type" :id="item.id" v-model="item.deleted"/>
</template>
</Liste>
</div>
Typescript:
import {Vue, Component, Prop} from 'vue-property-decorator'
export type AbstractElement = {
[key: string]: any // passed as $attrs | useable for assigned $props
ElementTag: string
slotName?: string
}
#Component<List>({
render(h){
let tag = this.tag
|| (this.$parent.$vnode && this.$parent.$vnode.tag)
|| (this.$parent.$el && this.$parent.$el.tagName)
if(tag === undefined) throw Error(`tag prperty: ${tag} is invalid. Scope within valid vnode tag or pass valid component/ html tag as property`)
return h(tag, this.items.map(item => {
const {ElementTag, slotName, ...attrs} = item;
console.log("slotName", slotName)
return (this.$scopedSlots[slotName || ElementTag]
&& this.$scopedSlots[slotName || ElementTag]({item}))
|| h(ElementTag, {
attrs: attrs,
slot: slotName || ElementTag,
scopedSlots: this.$scopedSlots
})
}))
}
})
export default class List extends Vue{
#Prop(String) readonly tag?: string
#Prop(Array) readonly items!: Array<AbstractElement>
}
will raise this here
You can use a Proxy to map the document property names during access.
Note
In my original answer, I used Proxy handlers for get and set, which is sufficient for plain javascript objects, but fails when used with Vue data properties because of the observer wrappers that Vue applies.
By also trapping has in the Proxy, this can be overcome. I left the original answer below for anyone interested in this problem.
Here is a demo of how to use Proxy to 'alias' Vue reactive properties to different names
without affecting the original data structure
without having to copy the data
console.clear()
Vue.config.productionTip = false
Vue.config.devtools = false
Vue.component('checkboxgroup', {
template: '#checkboxGroup',
props: { items: Array, required: true },
});
const aliasProps = (obj, aliasMap) => {
const handler = {
has(target, key) {
if (key in aliasMap) {
return true; // prevent Vue adding aliased props
}
return key in target;
},
get(target, prop, receiver) {
const propToGet = aliasMap[prop] || prop;
return Reflect.get(target, propToGet);
},
set(target, prop, value, receiver) {
const propToSet = aliasMap[prop] || prop;
return Reflect.set(target, propToSet, value)
}
};
return new Proxy(obj, handler);
}
new Vue({
el: '#app',
data: {
documents: [
{ id: 1, name: "Doc 1", deleted: false },
{ id: 2, name: "Doc 2", deleted: false },
{ id: 3, name: "Doc 3", deleted: true },
]
},
computed: {
checkBoxItems() {
const aliases = {
title: 'name',
state: 'deleted'
}
return this.documents.map(doc => aliasProps(doc, aliases));
}
},
methods: {
saveSettings: function() {
console.log(this.documents);
}
},
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" rel="stylesheet"/>
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet"/>
<div id="app">
<v-app id="theapp">
<v-container>
<checkboxgroup :items="checkBoxItems"></checkboxgroup>
<v-btn color="info"
#click="saveSettings">Save</v-btn>
</v-container>
</v-app>
</div>
<template id="checkboxGroup">
<v-container style="display: flex">
<v-checkbox
v-for="(item, index) in items"
:key="index"
v-model="item.state"
:label="item.title"
></v-checkbox>
</v-container>
</template>
Original answer
You can use a Proxy to map the document property names during access.
<template>
...
<CheckboxGroup :items="checkBoxItems"/>
...
</template>
<script>
export default {
...
computed: {
checkBoxItems() {
const handler = {
get: function(target, prop) {
return prop === 'title' ? target.name :
prop === 'state' ? target.deleted :
target[prop];
},
set(obj, prop, value) {
const propToSet =
prop === 'title' ? 'name' :
prop === 'state' ? 'deleted' :
prop;
obj[propToSet] = value;
}
};
return documents.map(doc => new Proxy(doc, handler))
},
},
...
}
</script>
Demo
const documents = [
{ id: 1, name: "Doc 1", deleted: false },
{ id: 2, name: "Doc 2", deleted: false },
{ id: 3, name: "Doc 3", deleted: true },
]
const handler = {
get: function(target, prop) {
return prop === 'title' ? target.name :
prop === 'state' ? target.deleted :
target[prop];
},
set(obj, prop, value) {
const propToSet =
prop === 'title' ? 'name' :
prop === 'state' ? 'deleted' :
prop;
obj[propToSet] = value;
}
};
const checkItems = documents.map(doc => new Proxy(doc, handler))
console.log('Accessing new property names via checkItems')
checkItems.forEach(ci => console.log(ci.id, ci.title, ci.state))
console.log('After update, values of documents')
checkItems.forEach(ci => ci.state = !ci.state )
documents.forEach(doc => console.log(doc.id, doc.name, doc.deleted))

Why `#change` trigger don't work for v-data-picker?

I use v-calendar package in my Vue.js application.
I want to send selected data range values to parent component. Why #change trigger don't work?
Parent.vue:
<template>
<div>
<Child #setRange="setRange" :range="range"/>
</div>
</template>
<script>
data() {
return {
range: this.range,
}
},
mounted() {
firstCallToPage();
},
methods: {
firstCallToPage(){
axios.get('URL').then(response => {
let self = this;
this.range = {
start: response.startDate,
end: response.endDate,
};
}
},
setRange(range_value) {
this.range = range_value;
}
}
</script>
Child.vue:
<v-date-picker class='v-date-picker'
mode='range'
v-model='rangeValue'
:show-day-popover=false
:max-date='new Date()'
show-caps
:input-props='{placeholder: "", readonly: true}'
#change="sendRange">
</v-date-picker>
props: {
range: {
type: Object,
},
},
data() {
return {
rangeValue: this.range
}
},
sendRange: function () {
this.$emit('setRange', this.rangeValue);
}
ERROR in console:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
Instead, use a data or computed property based on the prop's value. Prop being mutated: "range"
Try #input instead of #change. In v-datetime-picker works only with #input.
The error message is pretty explicit. The problem is that you give a prop to your child component (the one that contains v-date-picker) and you are overriding this prop with v-model (v-model is just syntactic sugar for :value and #change).
Derive your prop's value with a data value and use it for your operations:
<v-date-picker class='v-date-picker'
mode='range'
v-model='rangeValue'
:show-day-popover=false
:max-date='new Date()'
show-caps
:input-props='{placeholder: "", readonly: true}'
>
</v-date-picker>
props: {
range: {
type: Object,
},
},
data() {
return {
rangeValue: this.range
}
},
sendRange: function () {
this.$emit('setRange', this.rangeValue);
}
Instead of using a method, you can make use of watch...
Consider you have the following attributes in the range Object
range: {
start:value,
end: value
}
<v-date-picker class='v-date-picker'
mode='range'
v-model='rangeValue'
:show-day-popover=false
:max-date='new Date()'
show-caps
:input-props='{placeholder: "", readonly: true}'
>
</v-date-picker>
props: {
range: {
type: Object,
},
},
watch: {
'rangeValue.start': function(newVal){
this.$emit('setRange', newVal);
}
},
data() {
return {
rangeValue: this.range
}
}

Props not being assigned to data() attribute in Vue

I am creating a Vue component, which should refresh restaurants depending on user dynamically selected filters.
Therefor I have to update the filteredRestaurants in the data() function of my Vue component.
However, at first, when the Vue component is rendered, it takes the restaurant information from the "restaurants" prop.
I have tried to insert the "restaurants" into the filteredRestaurants data attribute to set it as a default value. Unfortunatelly then the stores wouldnt show at tall, as if the "restaurants" prop is inserted after the filteredRestaurants is assigned its value.
My question is, how can i get the "restaurants" prop into filteredRestaurants so that I can later on, re-render the Vue component when the user changes the filters.
<template lang="html">
<div class="test">
<Filters></Filters>
<div>
<ul class="o-list c-stores">
<Result v-bind:total="restaurants.length" v-bind:open="isOpen" v-on:toggle="toggleRestaurantList"></Result>
<li v-for="(restaurant, index) in restaurants" class="c-stores__location" :class="{'first': isFirst(index), 'last': isLast(index, restaurants)}">
<Location :index="index" :store="restaurant" :link="() => setCurrentRestaurant(restaurant)"></Location>
</li>
</ul>
</div>
</div>
</template>
<script>
import eventHub from './../../event-hubs/storefinder'
import Location from './Location'
import Filters from './Filters'
import Result from './Result'
export default {
props: ["restaurants", "isOpen", "currentSearch"],
data() {
return {
attributes : [],
// Here I am assigning the prop
filteredRestaurants : this.restaurants
}
},
head: {
title: function () {
return {
inner: this.$t('storefinder.overview')
}
},
meta: function functionName() {
return [{
name: 'og:title',
content: this.$t('storefinder.overview') + ' - ' + this.$t('storefinder.name'),
id: "og-title"
},
{
name: 'description',
content: this.$t('storefinder.description'),
id: "meta-description"
},
{
name: 'og:description',
content: this.$t('storefinder.description'),
id: "og-description"
},
]
}
},
components: {
Location,
Filters,
Result
},
methods: {
toggleRestaurantList() {
eventHub.$emit('showRestaurantList');
},
setCurrentRestaurant(restaurant) {
this.trackRestaurantSelect(restaurant.publicNameSlug);
this.$router.push({
name: "store",
params: {
restaurant: restaurant.publicNameSlug
}
});
},
trackRestaurantSelect(restaurantName) {
dataLayer.push({
'event': 'GAEvent',
'eventCategory': 'restaurants',
'eventAction': 'clickResult',
'eventLabel': restaurantName,
'eventValue': undefined,
'searchTerm': this.currentSearch && this.currentSearch.toLowerCase(),
'amountSearchResults': 1
});
},
created() {
eventHub.$on('addFilterTheRestaurants', (attribute) => this.attributes.push(attribute));
eventHub.$on('removeFilterTheRestaurants', (attribute) => this.attributes = this.attributes.filter(item => item !== attribute));
},
isLast: function (idx, list) {
return idx === list.length - 1;
},
isFirst: function (idx) {
return idx === 0;
},
}
}
</script>
The only way this worked, was when I had the filteredRestaurants as a function which returned "restaurants", and I called it inside the Vue template:
filteredRestaurants(){
return this.restaurants
}
Any help appreciated.
If I understand your question correctly, you are looking for a computed property:
computed: {
filteredRestaurants() {
return this.restaurants;
}
}
This will update whenever the value of this.restaurants changes.

Categories

Resources