Using v-tooltip inside v-menu activator in vuetify 2.0 - vue.js

How to use v-tooltip inside v-menu activator with vuetify 2.0? Previously it was working using slot="activator".
That's what I'm trying to combine:
<v-menu>
<template v-slot:activator="{on}">
<v-btn v-on="on">Menu Trigger</v-btn>
</template>
...list with menu options...
</v-menu>
and
<v-tooltip v-slot:activator="{on}">
<v-btn v-on="on">Menu Trigger with Tooltip</v-btn>
<span>Tooltip Content</span>
</v-tooltip>
Now I try to paste v-tooltip inside v-menu, what should happen with {on} here?

I think you're most likely unsure about the "conflicted" on objects passed to the template by multiple activator slots and how to apply all of the event handlers on the target element(s).
If that's the case, you can workaround this by assigning either one (or both) of them to a variable with a different name (see: assigning to new variable names), and then destructure and "restructure", which basically glues them back together (or merge them, technically speaking).
<v-menu>
<template #activator="{ on: onMenu }">
<v-btn v-on="onMenu">Menu Trigger</v-btn>
<v-tooltip bottom>
<template #activator="{ on: onTooltip }">
<v-btn v-on="{ ...onMenu, ...onTooltip }">Menu Trigger with Tooltip</v-btn>
</template>
<span>Tooltip Content</span>
</v-tooltip>
</template>
<!-- ...list with menu options... -->
</v-menu>
Or, use the slot props directly. Just make sure to name them properly so they won't introduce another naming conflict with the component's data and/or props.
<v-menu>
<template #activator="menu">
<v-btn v-on="menu.on">Menu Trigger</v-btn>
<v-tooltip bottom>
<template #activator="tooltip">
<v-btn v-on="{ ...menu.on, ...tooltip.on }">Menu Trigger with Tooltip</v-btn>
</template>
<span>Tooltip Content</span>
</v-tooltip>
</template>
<!-- ...list with menu options... -->
</v-menu>
Complete Demo:
new Vue({
el: '#app',
data: () => ({
items: [
{ title: 'Item #1' },
{ title: 'Item #2' },
{ title: 'Item #3' }
]
})
})
<link href="https://fonts.googleapis.com/css?family=Roboto:400|Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#1.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.x/dist/vuetify.js"></script>
<div id="app">
<v-menu>
<template #activator="{ on: onMenu }">
<v-btn v-on="onMenu">Menu Trigger</v-btn>
<v-tooltip bottom>
<template #activator="{ on: onTooltip }">
<v-btn v-on="{ ...onMenu, ...onTooltip }">Menu Trigger with Tooltip</v-btn>
</template>
<span>Tooltip Content</span>
</v-tooltip>
</template>
<v-list>
<v-list-tile
v-for="(item, index) in items" :key="index"
#click.prevent>
<v-list-tile-title>{{ item.title }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
</div>

Related

Open a dropdown with right click in Vue3 and Vuetify3

I am trying to work on the context menu for Vue3. I want to show the list of items under the v-menu by right-clicking on a dropdown button. I am working on this link as a reference but when I tried to do the same in my Vue3 file, It gives me an error like this-
Can anyone suggest what am I doing wrong? Here is the code-
<v-menu offset-y>
<template v-slot:activator="{ props }">
<v-btn
color="primary"
dark
v-bind="props"
#contextmenu.prevent="props.click"
>
Dropdown
</v-btn>
</template>
<v-list>
<v-list-item
v-for="(item, index) in menuClick"
:key="index"
#click=""
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
I couldn't discover any context menu event in Vue3's doc and Vuetify's menu API. Although, I have an alternative in my mind that can do this job. You can utilize the .right modifier sync with v-model to open the menu with the right click of a button.
Here is the strategy-
Use v-model to control the v-menu toggling status.
On the right-click, set the v-model to true so the menu will be open.
On the left-click (default) set the v-model to false. (Do this step only if you don't want to open the menu on the default click.)
Here is a functioning demo-
const { createApp } = Vue
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = createApp({
data: () => ({
menu: false,
items: [
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me 2' },
],
}),
}).use(vuetify).mount('#app')
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#3.1.2/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify#3.1.2/dist/vuetify.min.css">
<div id="app">
<v-app>
<div class="d-flex justify-space-around mb-5 font-weight-bold">
I will open only with the right-click.
</div>
<div class="d-flex justify-space-around">
<v-menu v-model="menu">
<template v-slot:activator="{ props }">
<v-btn
color="primary"
v-bind="props"
#click.left.prevent="menu = false"
#click.right.prevent="menu = true"
>
Dropdown
</v-btn>
</template>
<v-list>
<v-list-item
v-for="(item, index) in items"
:key="index"
:value="index"
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
</v-app>
</div>

How do I pass v-on/v-bind to component

I am working with Vuetify menu and button components. I am trying to pass the menu and attrs from the activator slot in the template in SomeFile.vue into the v-btn in the MyCustomButtonTemplate.vue file, but I cannot figure out how to do it. Does anyone know how to correctly pass those values? A bare bones example is shown below
SomeFile.vue
<v-menu offset-y>
<template v-slot:activator="{ on: menu, attrs }">
<MyCustomButtonTemplate v-on="menu" v-bind="attrs">
</template>
<v-list>
<v-list-item>
<v-list-item-title>Some Title</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
MyCustomButtonTemplate.vue
<template>
<v-tooltip top>
<template #activator="{ on: tooltip }">
<v-btn v-on="{...tooltip, ...menu}" v-bind="attrs">Blah</v-btn>
</template>
<span>Some tooltip</span>
</v-tooltip>
</template>
You can access component event listeners and attributes in $listeners and $attrs objects. Also you can pass them to children with additional event handlers and listeners by destructuring them.
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-btn v-on="{ ...on, ...$listeners }" v-bind="$attrs">Blah</v-btn>
</template>
<span>Some tooltip</span>
</v-tooltip>
Here is the working codesandbox
First of all you need to make inheritAttrs to false in custom component, because by default vue for bindings will apply to the root element of the child component as normal HTML attributes.
<script>
export default {
inheritAttrs: false,
}
</script>
then you can apply attributes and listeners on your element in custom component.
<template>
<v-tooltip top>
<template #activator="{ on: tooltip }">
<v-btn v-on="{ ...tooltip, ...$listeners }" v-bind="$attrs">Blah</v-btn>
</template>
<span>Some tooltip</span>
</v-tooltip>
</template>
BTW you didnt define menu and attrs as props but you used it like you have it in your component. Most of reserved methods and properties that we use in vue starts with $. Of course this is true only for Options API.

Keep v-menu open state when the activator is pressed again

I'm trying to make a custom v-autocomplete using v-text-field and v-menu.
What confuses me is that v-menu closes when you click on the v-text-field again. The first click on the v-text field opens the v-menu, and the second click closes it. I couldn't find any property that turns off toggle mode.
Although, if you look at the html markup, v-menu is used in the v-select and v-autocomplete components, which close when you click on the content or outside the menu.
Codepen
<template>
<div id="app">
<v-app id="inspire">
<div class="text-center">
<v-main>
<v-container>
<v-row>
<v-col cols="3">
<v-menu bottom offset-y max-height="304px">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="model && model.title"
dense color="primary"
v-bind="attrs" v-on="on"
label="Choose" outlined>
</v-text-field>
</template>
<v-list dense>
<v-list-item-group v-model="model"
color="primary">
<v-list-item v-for="(item, index) in items"
:key="item.id" :value="item"
dense>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list-item-group>
</v-list>
</v-menu>
</v-col>
</v-row>
</v-container>
</v-main>
</div>
</v-app>
</div>
</template>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [
{id:1, title: 'Click Me 1' },
{id:2, title: 'Click Me 2' },
{id:3, title: 'Click Me 3' },
{id:4, title: 'Click Me 4' },
],
model: null
}),
})
</script>

Vuetify access v-slot:item in datatable custom component

I've made a custom datatable component, where my Table.vue file is shown below:
<template>
<div>
<v-data-table
:headers="headers"
:items="items"
:search="search"
:loading="loading"
loading-text="Loading... Please wait"
dense
>
<template v-slot:top>
<v-toolbar dark flat dense>
<v-toolbar-title>{{ title }}</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
<v-spacer></v-spacer>
</v-toolbar>
</template>
</v-data-table>
</div>
</template>
<script>
export default {
name: "Table",
props: [
"headers",
"items",
"title",
"itemsPerPage",
"loading",
],
data: function () {
return {
search: '',
}
},
methods: {
},
};
</script>
And I'm using it like that:
<Table
:headers="headers"
:items="groups"
:loading="loading"
title="Baseny"
>
</Table>
Everything is fine, but I want to add custom columns with actions for every input (every input has different ID)
Normally (without a custom component) I'd use the following code:
<v-data-table
:headers="headers"
:items="times"
:items-per-page="5"
:search="search"
:loading="loading"
loading-text="Ładowanie... Proszę czekać"
>
<template v-slot:top>
<v-toolbar dark flat dense>
<v-toolbar-title>Lista zajęć</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Szukaj"
single-line
hide-details
></v-text-field>
<v-spacer></v-spacer>
<v-btn
color="primary"
:to="{ name: 'admin.times.create' }"
>
Dodaj zajęcie
</v-btn>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon
small
class="mr-2"
#click="show(item)"
>
mdi-pool
</v-icon>
<v-icon
small
class="mr-2"
#click="edit(item)"
>
mdi-pencil
</v-icon>
</template>
</v-data-table>
I'm using this v-slot:
<template v-slot:item.actions="{ item }">
<v-icon
small
class="mr-2"
#click="show(item)"
>
mdi-pool
</v-icon>
<v-icon
small
class="mr-2"
#click="edit(item)"
>
mdi-pencil
</v-icon>
</template>
However, when I wrote the custom reusable table components it didn't work when I put these lines into tag.
How can I use my custom components properly in this scenario?
What you want to achieve is I believe a wrapper component. You want to wrap a component on top of another one to let him have some custom properties that you want to reuse in your application.
What you need is a small snippet that will allow your slots to be used:
<!-- pass through scoped slots -->
<template v-for="(_, scopedSlotName) in $scopedSlots" v-slot:[scopedSlotName]="slotData">
<slot :name="scopedSlotName" v-bind="slotData" />
</template>
<!-- pass through normal slots -->
<template v-for="(_, slotName) in $slots" v-slot:[slotName]>
<slot :name="slotName" />
</template>
You can find the source of this here
Basically, here how you can rewrite your CustomTable.vue:
<template>
<div>
<v-data-table
v-bind="$attrs"
v-on="$listeners"
:search="search"
loading-text="Loading... Please wait"
dense
>
<!-- pass through scoped slots -->
<template
v-for="(_, scopedSlotName) in $scopedSlots"
v-slot:[scopedSlotName]="slotData"
>
<slot :name="scopedSlotName" v-bind="slotData" />
</template>
<!-- pass through normal slots -->
<template v-for="(_, slotName) in $slots" v-slot:[slotName]>
<slot :name="slotName" />
</template>
<template v-slot:top>
<v-toolbar dark flat dense>
<v-toolbar-title>{{ title }}</v-toolbar-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
<v-spacer></v-spacer>
</v-toolbar>
</template>
</v-data-table>
</div>
</template>
<script>
export default {
name: "CustomTable",
props: ["title"],
data: function () {
return {
search: "",
};
},
methods: {},
};
</script>
I made a codesandbox to show you how it works:
https://codesandbox.io/s/vuetify-2-forked-3lp9y?file=/src/components/CustomTable.vue
I also added automatic attribute and listeners bindings on your table, to allow you to use all the features that Vuetify provides.

VueJs Vuetify Autocomplete with links

I'am using vuetify and i want make auto complete like the one on their official site.
But i'm faces some issues :
The items value not appear in the return list and i don't know how to make appear the links.
Here is my code.
Thanks You
<template>
<v-app>
<v-app-bar app color="primary" dark>
<v-app-bar-nav-icon #click="drawer = true"></v-app-bar-nav-icon>
<v-toolbar-title> Board </v-toolbar-title>
<v-spacer></v-spacer>
<v-autocomplete
:loading="loading"
:filter="v => v"
:items="items"
:search-input.sync="search"
v-model="select"
flat hide-no-data hide-details return-object placeholder="Search ...">
<v-list-tile
slot="prepend-item"
class="grey--text">
{{ items.length }} menus found
</v-list-tile>
<template slot="selection" slot-scope="{ item }">
{{item.name}} {{item.url}}
</template>
<template slot="item" slot-scope="{ item }">
<v-list-tile-content>
<p class='fullName'>{{item.name}} {{item.url}}</p>
</v-list-tile-content>
</template>
</v-autocomplete>
</v-app-bar>
<v-main>
<HelloWorld/>
</v-main>
</v-app>
I went through your issue and try to make a codepen and it worked.
My suggestion is that you should consider data structure when you use object with auto complete and it 's needed even you use v-select.
Please check this pen. https://codepen.io/endmaster0809/pen/qBZRywZ
items: [
{
name: 'Alerts',
url: 'https://vuetifyjs.com/en/components/alerts/'
},
{
name: 'Autocompletes',
url: 'https://vuetifyjs.com/en/components/autocompletes/#autocompletes'
}
],
select: {
name: 'Alerts',
url: 'https://vuetifyjs.com/en/components/alerts/'
},