How do I pass v-on/v-bind to component - vue.js

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.

Related

Vuetify v-menu not expanding when component changes (Vue router)

I have a Vuetify v-menu on my App.vue, which appears on every page. When loading a page for the first time, it correctly expands (the menu is used for options such as Sign Out, Account Settings etc). When I navigate to another component (using Vue router), the v-menu does not expand when the new page is loaded. It doesn't work even if I click it (not just hover). What could be the cause of this?
...
<div v-if="signedIn" class="text-center hideOnMobile">
<v-menu
open-on-hover
bottom
offset-y
>
<template v-slot:activator="{ on, attrs }">
<v-btn
elevation='0'
color="primary"
dark
v-bind="attrs"
v-on="on"
class="userDropDown"
>
{{ username }}
<v-icon dark left>mdi-chevron-down</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item
v-for="(item, index) in items"
#click="selectSection(item.title)"
:key="index"
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
...
<router-view :uid='this.uid' :username='this.username' :userData='this.userData'></router-view>

Converting Vuetify 1.x v-slot activator to 2.x via v-data-table

I have a piece of working code from Veutify 1.5.6 that includes an icon inside of a data table that, when clicked, displays a menu. When clicked the activator shows the list that was activated.
I have a codepen with this example
I want the same functionality, but Vuetify 2 does not have slot="activator".
It looks like they use v-slot:activator="{ on }" with a template tag, but I can't get this replicated over.
This is my attempt at it.
<v-data-table
:headers="labels"
:items="data"
>
<template v-slot:[`item.statusIcon`]="{ item }">
<td style="cursor: pointer;">
<v-icon :class="item.status">{{item.statusIcon}}
<template v-slot:activator="{ on }">
<v-list>
<v-list-item
v-on="on"
v-for="(status, index) in statusItems"
:key="index"
:class="status.title"
#click="setStatus(item, status.title)"
>{{ status.title }}></v-list-item>
</v-list>
</template>
</v-icon>
</td>
</template>
</v-data-table>
There's definitely something small I'm missing and I know that the v-icon should be under the template, but I keep getting this error
'v-slot' directive must be owned by a custom element, but 'td', 'div', 'template' is not
I found this thread, but still no help.
Any help would be appreciated
You should wrap the icon with v-menu component then use this icon as menu activator :
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-icon v-on="on" :class="item.status">{{item.statusIcon}}
</v-icon>
</template>
<v-list>
<v-list-item
v-for="(status, index) in statusItems"
:key="index"
:class="status.title"
#click="setStatus(item, status.title)"
>{{ status.title }}></v-list-item>
</v-list>
</v-menu>

Datepicker in vuetify with validators

I need to create a new component that only have a datepicker with his validators. This component is generic because I need to use in more components easily. I need that when you click into the datepicker and you don't pick any date show a red validation like this:
and then:
If you pick any date, this validation should disappear.
I had thinked in this form:
<ValidationObserver v-slot="{ handleSubmit, invalid }" ref="form">
<v-form #submit.prevent="handleSubmit(submit)">
<ValidationProvider ref="form" :name="nombre" :rules="rules" v-slot="{ errors }">
<v-menu v-model="dialogOpen" :close-on-content-click="false" :nudge-right="40" transition="scale-transition"
offset-y min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field :error-messages="errors" v-model="value" :label="label" prepend-icon="mdi-calendar" v-bind="attrs" v-on="on">
</v-text-field>
</template>
<v-date-picker required v-model="value" #input="dialogOpen = false" #change="changed"
#keydown="checkValidate"></v-date-picker>
</v-menu>
</ValidationProvider>
</v-form>
But this doesn't work. Someone can share me another example or solution for this thing?
Thanks!
The problem is that vee-validate (v3) uses v-model to find the input tags, and since vuetify uses v-model for various components to compose them together like the date-picker, vee-validate will validate the menu instead of the v-text-field.
There is a workaround, you need to wrap the Vuetify's picker snippet in a new component DatePicker.vue
<v-menu v-model="dialogOpen" :close-on-content-click="false" :nudge-right="40" transition="scale-transition" offset-y min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field :error-messages="errors" v-model="value" :label="label" prepend-icon="mdi-calendar" v-bind="attrs" v-on="on">
</v-text-field>
</template>
<v-date-picker required v-model="value" #input="dialogOpen = false" #change="changed" #keydown="checkValidate"></v-date-picker>
</v-menu>
Note that you still need to implement v-model support for this new component by emitting input event when value changes.
And then use the custom DatePicker with the ValidationProvider:
<ValidationProvider ref="form" :name="nombre" :rules="rules" v-slot="{ errors }">
<DatePicker v-model="value" />
</ValidationProvider>
Of course that was a shortsight on vee-validate's part, and the new v4 release for Vue 3 avoids this bad design.

Using v-tooltip inside v-menu activator in vuetify 2.0

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>

Vuetify tooltip doesn't show up in the DOM

When pasting the docs' example in my component's template:
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-btn color="primary" dark v-on="on">Button</v-btn>
</template>
<span>Tooltip</span>
</v-tooltip>
... I get this in the DOM:
<span data-v-26084dc2="" class="v-tooltip v-tooltip--top"></span>
This is how I import Vuetify in my main.js:
import Vuetify from 'vuetify';
import 'vuetify/dist/vuetify.min.css';
Vue.use(Vuetify);
Using Vue 2.5.17 and Vuetify 1.3.15
v-slot was introduced in Vue 2.6.0. You'll need to use the older slot
syntax with 2.5.17 and use it directly on <v-btn> element
vuejs.org/v2/guide/components-slots.html#Deprecated-Syntax
slot attribute can also be used directly on a normal element
<v-tooltip bottom>
<v-btn slot="activator" color="primary" dark>Button</v-btn>
<span>Tooltip</span>
</v-tooltip>