What is the purpose of {attrs} parameter in v-slot: activator? - vue.js

I created a simple v-dialog using Vuetify. It uses v-slot:activator with destructured data, namely {on, attrs }. While I can understand on part then I can't get what's the purpose of attrs? If I remove it then dialog still works fine. How it works and what is it for?
Here some basic example from Vuetify docs:
<template>
<div class="text-center">
<v-dialog
v-model="dialog"
width="500"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
color="red lighten-2"
dark
v-bind="attrs"
v-on="on"
>
Click Me
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5 grey lighten-2">
Privacy Policy
</v-card-title>
<v-card-text>
Lorem ipsum dolor sit amet...
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
data () {
return {
}
},
}
</script>

Its purpose is to give you set of attributes/props that you can easily bind with v-bind="attrs" (using object binding syntax) to the component you choose as an activator
In the case of v-dialog its content is generated by default implementation of activatable mixin and generates some ARIA attribute but the component using the mixin can override or extend it (as for example v-menu does)

Related

Instance of v-dialog is generated twice – Vuetify

I have a problem with <v-dialog> that appears everywhere in my Vue.js application using Vuetify.js.
Every <v-dialog> is loading twice on the page. You can see it by looking at how it appears:
If I close one dialog the other dialog stays visible and works exactly like the first. It seems like Vue is generating two instances of it.
The code of the dialog isn't the issue, I think:
<v-dialog
v-model="dialog"
persistent
hide-overlay
no-click-animation
>
<v-card>
<v-card-title>Text</v-card-title>
<v-card-text>
<p class="subtitle-1">Some more text</p>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="primary"
depressed
>
Next
<v-icon right>mdi-chevron-right</v-icon>
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
The v-model is declared in data():
export default {
data() {
return {
dialog: true,
};
},
}
Any help is appreciated! Thanks.

v-list in v-list not working with vuetify

I am trying to implement list in list using vuetify. But I didnt manage to make it work. Below is the code and sample data. Please help me make it work. I am using vuetify 2.3.10. If I comment out the part for the second list then the first list works
<template>
<div>
<v-toolbar id="filter">
<v-layout row wrap>
<v-flex xs12 md1>
<v-menu attach="#filter-users" offset-y>
<template v-slot:activator="{ on, attrs }">
<div class="dropdown" v-bind="attrs" v-on="on">User</div>
</template>
<v-list id="filter-users">
<v-list-item
v-for="search_user in search_users"
#click="filterUser({user: search_user})">
<v-list-item-title>{{ search_user.name }}</v-list-item-title>
</v-list-item>
<v-list v-if="search_user.sub_users.length">
<v-list-item v-for="(search_sub_users, index) in search_user.sub_users"
:key="`sub_user_${index}`"
#click="filterUsers({user: search_sub_user})">
<v-list-item-title>{{ search_sub_user.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-list>
</v-menu>
</v-flex>
</v-layout>
</v-toolbar>
</div>
</template>
search_users demo data
search_users:[{
name: 'James',
sub_users: [
{
name: 'Willy'
},
{
name: 'Jack'
},
}]
},
name: 'Rock',
sub_users: [
{
name: 'Randy'
},
{
name: 'Amy'
},
}]
],
There's a lot of things going on in your code so let me enumerate them for you:
On your second sub-list, it can't find the search_user variable since that variable is only accessible inside the <v-list-item/>, which is outside of the <v-list/>.
What you can do is to create a <template/> that will wrap both <v-list-item/> and <v-list/> then put the v-for there. Something like this:
<v-list ...>
<template v-for="(search_user, index) in search_users">
<v-list-item :key="`user_${index}`">...</v-list-item> <!-- User List -->
<v-list :key="`sub_user_${index}`">...</v-list> <!-- Sub User List -->
</template>
</v-list>
Also, don't forget the v-bind:key, or simply :key, to the v-for's child elements.
You attempt to attach the <v-menu/> to its default slot. The attach prop of <v-menu/> should be somewhere outside the <v-menu/> or inside the its activator slot. However, it should attach directly to the element in your activator slot by default so you can just omit the attach prop.
<v-menu offset-y> <!-- remove the `attach` prop -->
<template v-slot:activator="{ on, attrs }">
<div class="dropdown" v-bind="attrs" v-on="on">User</div>
</template>
<v-list>...</v-list>
</v-menu>
On your second sub-list, you mistakenly typed search_sub_user instead of search_sub_users:
v-for="(search_sub_users, index) in search_user.sub_users"` // wrong
v-for="(search_sub_user, index) in search_user.sub_users"` // right
Here is a refactored version of your code at codesandbox.

how I can change the background color by clicking button using vuetify?

Here, I have used vuetify to create buttons which pop up dialog box that contains buttons with different colors and I want to use buttons to change the background color of navigation and menu bar but due to my limited knowledge on vue and vuetify I am not able to change my background color. Can anyone help me???
My code is here:
<template>
<v-row justify="center">
<v-dialog v-model="dialog" persistent max-width="600px" class="pa-md-4 mx-lg-auto">
<template v-slot:activator="{ on }">
<v-btn color="primary" dark v-on="on">Theme</v-btn>
</template>
<div class="text-center">
<v-btn v-for="(item) in colors" :color="item" dark large class="ma-2" style="width:100px;">
<span class="text-truncate" style="width:120px;">
{{item.replace(/-/g, '')}}
</span>
</v-btn>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="dialog = false">Close</v-btn>
<v-btn color="blue darken-1" text #click="dialog = false">Save</v-btn>
</v-card-actions>
</div>
</v-dialog>
</v-row>
</template>
<script>
export default {
data(){
return{
colors: ["red","pink","purple","deep-purple","indigo","blue","light-blue","cyan","teal","green","light-green","lime","yellow","amber","orange","deep-orange","brown","blue-grey","grey", "black"],
dialog: false
}
},
} </script>
To do this you actually use themes that you setup in vuetify.js and you can assign different colors to the different values for example primary: '#c3c3c3' and define different themes within your app. It comes with light and dark theme but you can also add/remove themes programmatically.
It's quite a bit of code so I will post the links on where directions can be found here.
https://vuetifyjs.com/en/customization/theme/#custom-theme-variants
And the actual usage can be found here:
https://vuetifyjs.com/en/customization/theme/#usage

How can I custom datepicker in the vuetify?

I want to make datetimepicker like this : https://vuetifyjs.com/en/getting-started/consulting-and-support. If you click "2 Hour Consulting Session", it will display datetimepicker like this : https://ibb.co/kHrbHTG. I want to make like that. But I don't find the component datetimepicker in the documentation. The docs : https://vuetifyjs.com/en/components/date-pickers
Based on my analysis, it uses datepicker modal. not the datepimepicker. The timepicker is only customized using the outline button. but I am confused how to place the outline button next to the datepicker
My script like this :
<template>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-dialog
ref="dialog"
v-model="modal"
:return-value.sync="date"
persistent
width="290px"
>
<template v-slot:activator="{ on }">
<v-btn color="success" dark v-on="on">Call datepicker</v-btn>
</template>
<v-date-picker v-model="date" scrollable>
<div class="flex-grow-1"></div>
<v-btn text color="primary" #click="modal = false">Cancel</v-btn>
<v-btn text color="primary" #click="$refs.dialog.save(date)">OK</v-btn>
</v-date-picker>
</v-dialog>
</v-col>
</v-row>
</template>
<script>
export default {
data: () => ({
modal: false,
}),
}
</script>
Is it possible to customize the datepicker to be like that?
That Datepicker is not part of Vuetify. That screenshot you took is in fact an <iframe> of the https://calendly.com/ website, which looks like a website for managing appointments. So that calendar is basically just custom CSS made by that company, nothing to do with Vuetify.

Meaning of v-slot:activator="{ on }"

Looking at the Vuetify example code for v-toolbar, what is the purpose of v-slot:activator="{ on }"? For example:
<template v-slot:activator="{ on }">
<v-toolbar-title v-on="on">
<span>All</span>
<v-icon dark>arrow_drop_down</v-icon>
</v-toolbar-title>
</template>
<script>
export default {
data: () => ({
items: [
'All', 'Family', 'Friends', 'Coworkers'
]
})
}
</script>
As far as I can see, on is not a defined variable anywhere, so I don't see how this is working. When I try it in my project, Internet Explorer throws an error on the <template v-slot:activator="{ on }">, but if I remove it, the page renders.
You're likely referring to this example:
<v-toolbar color="grey darken-1" dark>
<v-menu :nudge-width="100">
<template v-slot:activator="{ on }">
<v-toolbar-title v-on="on">
<span>All</span>
<v-icon dark>arrow_drop_down</v-icon>
</v-toolbar-title>
</template>
...
</v-menu>
</v-toolbar>
The following line declares a scoped slot named activator, and it is provided a scope object (from VMenu), which contains a property named on:
<template v-slot:activator="{ on }">
This uses destructuring syntax on the scope object, which IE does not support.
For IE, you'd have to dereference on from the scope object itself:
<template v-slot:activator="scope">
<v-toolbar-title v-on="scope.on">
But the ideal solution IMO is to use a Vue CLI generated project, which includes a Babel preset (#vue/babel-preset-app) to automatically include the transforms/polyfills needed for the target browsers. In this case, babel-plugin-transform-es2015-destructuring would be automatically applied during the build.
Details on the activator slot
VMenu allows users to specify a slotted template named activator, containing component(s) that activate/open the menu upon certain events (e.g., click). VMenu provides listeners for those events via an object, passed to the activator slot:
<v-menu>
<template v-slot:activator="scopeDataFromVMenu">
<!-- slot content goes here -->
</template>
</v-menu>
The slot content can access VMenu's event listeners like this:
<v-menu>
<template v-slot:activator="scopeDataFromVMenu">
<button v-on="scopeDataFromVMenu.on">Click</button>
</template>
</v-menu>
For improved readability, the scoped data can also be destructured in the template:
<!-- equivalent to above -->
<v-menu>
<template v-slot:activator="{ on }">
<button v-on="on">Click</button>
</template>
</v-menu>
The listeners from the scope object are passed to the <button> with v-on's object syntax, which binds one or more event/listener pairs to the element. For this value of on:
{
click: activatorClickHandler // activatorClickHandler is an internal VMenu mixin
}
...the button's click handler is bound to a VMenu method.
I think the original question is about understanding the "on" object. It is best explained here:
https://github.com/vuetifyjs/vuetify/issues/6866
Essentially "on" is a prop passed in from the activator. What v-on="on" does is bind that on prop to the component. "on" itself is all of the event listeners passed from the activator.
To call out a readability tip, it's possible to use this syntax:
<v-menu>
<template v-slot:activator="{ on: activationEvents }">
<v-btn v-on="activationEvents">
I like turtles 🐢
</v-btn>
</template>
</v-menu>
In my brain this has a more fluent readability than v-on="on", which to me is like observing a conversation consisting solely of:
Person 1: "Hey"
Person 2: "Yep"
Understand? ;)
By the way, activationEvents could be any alias, like "slotEvents", "listeners", "anyOldEvent", or whatever makes more sense to the reader as a renaming of the mysterious on.
Run the below code,you will know what is 'attrs' an 'on' in v-menu.
<v-menu>
<template v-slot:activator="{ on, attrs }">
<div v-bind="attrs" v-on="on">
v-menu slot activator:
<br />
attrs == {{ JSON.stringify(attrs) }}
<br />
on == {{ '{' + Object.keys(on).map(k => k + " : " + on[k]).join(',') + '}' }}
</div>
</template>
</v-menu>
Result:
v-menu slot activator:
attrs == {"role":"button","aria-haspopup":true,"aria-expanded":"false"}
on == {
click:function (e) {if (_this.openOnClick) {onClick && onClick(e);}_this.absoluteX = e.clientX;_this.absoluteY = e.clientY;},
keydown:function () { [native code] }
}
Explanation:
<div v-bind="attrs" v-on="on"> equals
<div
v-bind="{role:'button',aria-haspopup:true,aria-expanded:'false'}"
v-on="{click:function (e) {/*implement by v-menu*/},keydown:function () {/*implement by v-menu*/}}"
>
Starting in vue 2.4.0+, v-on also supports binding to an object of event/listener pairs without an argument. Note when using the object syntax, it does not support any modifiers.
Example:
<!-- v-on's object syntax (vue 2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
About <template> tags in Internet Explorer throws an error :
as vuetify docs say:
Template caveat
Due to Internet Explorer’s limited support for <template> tags, you must send fully compiled dom elements to the browser. This can be done by either building your Vue code in advance or by creating helper components to replace the dom elements. For instance, if sent directly to IE, this will fail:
<!-- Vue Component -->
<template v-slot:items="props">
<td>{‌{ props.item.name }‌}</td>
</template>