Vuetify: [Documentation] v-slot:activator="{ on }"

Created on 29 Mar 2019  Β·  39Comments  Β·  Source: vuetifyjs/vuetify

https://vuetifyjs.com/en/components/tooltips#usage

<v-tooltip bottom>
      <template v-slot:activator="{ on }">
        <v-icon color="primary" dark v-on="on">home</v-icon>
      </template>
      <span>Tooltip</span>
</v-tooltip>

So I'm guessing that the activator slot in the component binds the data object from the render function so that its available in the parent scope. The data object contains an on property which includes the event listeners.

It'd be nice for the doc to provide some pointers to the logic and syntax used. A brief comment in the activator slot description with a link to the below url would be nice.

https://vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth

_Originally posted by @YuqiaoS in https://github.com/vuetifyjs/vuetify/issues/6823#issuecomment-476560266_

documentation

Most helpful comment

If we're looking at this syntax:

<v-dialog v-model="visible">
  <template v-slot:activator="{ on }">
    <v-btn v-on="on">

and we look at the following

The documentation for scoped slots

Now when we use the \

<todo-list v-bind:todos="todos">
  <template v-slot:todo="{ todo }">
    <span v-if="todo.isComplete">βœ“</span>
    {{ todo.text }}
  </template>
</todo-list>

The documentation for the v-on api

<!-- object syntax (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>

The documentation for vm.$listeners

Contains parent-scope v-on event listeners (without .native modifiers). This can be passed down to an inner component via v-on="$listeners" - useful when creating transparent wrapper components.

The source code for the v-dialog component

        genActivator: function genActivator() {
            var _this3 = this;

            if (!this.hasActivator) return null;
            var listeners = this.disabled ? {} : {
                click: function click(e) {
                    e.stopPropagation();
                    _this3.getActivator(e);
                    if (!_this3.disabled) _this3.isActive = !_this3.isActive;
                }
            };
            if ((0, _helpers.getSlotType)(this, 'activator') === 'scoped') {
                var activator = this.$scopedSlots.activator({ on: listeners });
                this.activatorNode = activator;
                return activator;
            }
            return this.$createElement('div', {
                staticClass: 'v-dialog__activator',
                class: {
                    'v-dialog__activator--disabled': this.disabled
                },
                ref: 'activator',
                on: listeners
            }, this.$slots.activator);
        }

I read the above syntax as saying

Expose the on event listeners object from the parent v-dialog component's activator slot to the child template. Using the v-on object syntax bind all the fields of the on event listeners object to the v-btn component's events. When the v-btn component is clicked, the function associated with the click field of the on event listeners object will be called, causing the v-dialog component to become visible.

What I think is causing the confusion with the syntax

The combination of the following

  • v-slot:activator="{ on }" is exposing the object on via object destructuring.

    • It is not intuitive that on is itself an object and not something else.

    • It is not intuitive where on is coming from

  • v-on="on" is leveraging the new v-on object syntax to automatically assign events on the v-btn component to functions associated with fields of the on object

If we rearrange the syntax slightly it is a bit more intuitive what is going on as it only requires knowledge of how slot scopes can pass data to their children by automatically communicating that on is an object with a click field that is getting associated with the v-btn click event.

  <v-dialog v-model="visible">
    <template v-slot:activator="{ on: { click } }">
      <v-btn v-on:click="click">

All 39 comments

@YuqiaoS No, it binds slot scope (values that are passed to slot scope), not all data.

This isn't clear at all. You should add doc to explain what the syntax means.

But that doesn't tell you that there's the "on" property that the slots pass to the parents that contains some built in listeners that you don't know of.

Scoped slot passes things that are passed by component

All v-tooltip slots aren't scoped, you can't get anything from them via this method

And you don't know what component passes to the slot if you don't read the source code, I agree that there should be explained what data is passed, probably in slots section
@sh7dm activator slot is (can be) scoped

Example code doesnt work right away. All examples are using same structure and docs are not helping as usual.

This has also confused me. I'm not sure how to create & use tooltips in my own components. Seems like a tooltip should have a default slot for the main component, and a named slot for the tooltip itself. The documentation doesn't really have much explanation of how it works.

this v-on="on" confuses me so so much.

I have been digging Vuejs Documentation for a long time and no luck. I don't know what it means.

I thoughtv-on:click is the common usage, or @click.

So, what the heck is v-on="on" ? what is on ?

@yuminatwca if you look at at the example you will see that on is a prop passed in from the activator. all v-on="on" is doing is bind that on prop to the component. on itself is all of the event listeners passed from the activator.

I have been digging Vuejs Documentation for a long time and no luck

Also this can be reported in vue docs repo

The question is not (I think) what "v-on" means; that's standard Vue. The question is why do I have to pass something called "on" around just to get a tooltip. My component doesn't have anything called "on", so what is the "on" object, why is it needed, is that name part of the API or is it arbitrary, is it useful to me or is it just boilerplate? These are the questions at hand I think.

According to vuejs documentation, a new syntax has been introduced since 2.4.0+:

<!-- object syntax (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>

But, what is confusing is v-on={ on }.

OK, here is the question: on what? on which event triggered ?

The question is not (I think) what "v-on" means; that's standard Vue. The question is why do I have to pass something called "on" around just to get a tooltip. My component doesn't have anything called "on", so what is the "on" object, why is it needed, is that name part of the API or is it arbitrary, is it useful to me or is it just boilerplate? These are the questions at hand I think.

Accurate.

I attempted to implement a date picker yesterday, adhering to the samples provided, and it was breaking on this mysterious "on" construct; only guessing as to why, I decided it must be time to drag my vue and vuetify packages up to latest versions. (Several hours of dependency mazes later) I got it working.

so what is the "on" object, why is it needed, is that name part of the API or is it arbitrary, is it useful to me or is it just boilerplate?

Fwiw, it does evidently only function with v-slot:activator="{ on }" and v-on="on" explicitly; when I tried v-slot:activator="{ onit }" and v-on="onit", it would not function.

Up until I have seen this, I was thinking vuetify makes readable codebase ;)

Far better to write it like this if possible

            <v-tooltip bottom v-for="(link, index) in project.links">
              <v-btn :href="link.url" :key="index" class="mx-3" icon
                     slot="activator" style="color: var(--primary-color)" target="_blank">
                <v-icon size="50px">{{ link.icon }}</v-icon>
              </v-btn>
              <span>{{link.tip}}</span>
            </v-tooltip>

sure but slot="activator" won't work anymore in vuetify 2.0

And it adds an extra html element which can sometimes break the layout

If we're looking at this syntax:

<v-dialog v-model="visible">
  <template v-slot:activator="{ on }">
    <v-btn v-on="on">

and we look at the following

The documentation for scoped slots

Now when we use the \

<todo-list v-bind:todos="todos">
  <template v-slot:todo="{ todo }">
    <span v-if="todo.isComplete">βœ“</span>
    {{ todo.text }}
  </template>
</todo-list>

The documentation for the v-on api

<!-- object syntax (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>

The documentation for vm.$listeners

Contains parent-scope v-on event listeners (without .native modifiers). This can be passed down to an inner component via v-on="$listeners" - useful when creating transparent wrapper components.

The source code for the v-dialog component

        genActivator: function genActivator() {
            var _this3 = this;

            if (!this.hasActivator) return null;
            var listeners = this.disabled ? {} : {
                click: function click(e) {
                    e.stopPropagation();
                    _this3.getActivator(e);
                    if (!_this3.disabled) _this3.isActive = !_this3.isActive;
                }
            };
            if ((0, _helpers.getSlotType)(this, 'activator') === 'scoped') {
                var activator = this.$scopedSlots.activator({ on: listeners });
                this.activatorNode = activator;
                return activator;
            }
            return this.$createElement('div', {
                staticClass: 'v-dialog__activator',
                class: {
                    'v-dialog__activator--disabled': this.disabled
                },
                ref: 'activator',
                on: listeners
            }, this.$slots.activator);
        }

I read the above syntax as saying

Expose the on event listeners object from the parent v-dialog component's activator slot to the child template. Using the v-on object syntax bind all the fields of the on event listeners object to the v-btn component's events. When the v-btn component is clicked, the function associated with the click field of the on event listeners object will be called, causing the v-dialog component to become visible.

What I think is causing the confusion with the syntax

The combination of the following

  • v-slot:activator="{ on }" is exposing the object on via object destructuring.

    • It is not intuitive that on is itself an object and not something else.

    • It is not intuitive where on is coming from

  • v-on="on" is leveraging the new v-on object syntax to automatically assign events on the v-btn component to functions associated with fields of the on object

If we rearrange the syntax slightly it is a bit more intuitive what is going on as it only requires knowledge of how slot scopes can pass data to their children by automatically communicating that on is an object with a click field that is getting associated with the v-btn click event.

  <v-dialog v-model="visible">
    <template v-slot:activator="{ on: { click } }">
      <v-btn v-on:click="click">

we rearrange the syntax slightly it is a bit more intuitive what is going on as it only requires knowledge of how slot scopes can pass data to their children by automatically communicating that on is an object with a click field that is getting associated with the v-btn click event.

  <v-dialog v-model="visible">
    <template v-slot:activator="{ on: { click } }">
      <v-btn v-on:click="click">

This would also help with using these slot scopes with children that are not native elements:

 <v-dialog v-model="visible">
    <template v-slot:activator="{ on: { click } }">
      <custom-element v-on:click.native="click">

_Offtopic, but kinda related since this thread is caused by complexity and confusion..._

Is it super crazy to desire a simple version of tooltips?

<v-tooltip tooltip="Text">
   <v-icon>home</v-icon>
</v-tooltip>

Simple text content on hover is, probably, the most common usage of a component.

Actually if you use the 'v-tooltip' npm module at https://www.npmjs.com/package/v-tooltip, it has even simpler syntax because it's just a directive:

              <v-icon @click="logOut"
                      v-tooltip='"Log out"'>
                logout
              </v-icon>

It's not totally material-design but you can style it pretty well.

@garyo that is a nice example of how a simple tooltip could be declared!
Yet, I'd like to stick to a chosen library as much as possible. Standalone packages tend to have their own merits (in v-tooltip case it is unjustified bundle weight).

So, hopefully, tooltip API will be simpler one day.
Maybe it is worth opening a new thread and start working towards API simplification.
Even though this conversation seems to go in circles - #2961

πŸ’€ Important to note the example :

  <v-dialog v-model="visible">
    <template v-slot:activator="{ on: { click } }">
      <v-btn v-on:click="click">

uses "click"; it is not same:same
it would not work on a phone or device without a mouse potentially creating a poor inconsistent across device user experience ( ... so hopefully that's what you wanted). ❀🐭 cheers!

@elasticdotventures What exactly doesn't work? https://codepen.io/anon/pen/voYNwb

After reading all the messages above, I still have some confusion πŸ˜• .
I have two nested elements both having activators:

<v-card>
      <v-card-text>
        <v-tooltip bottom>
          <template v-slot:activator="{ on }">
            <v-layout align-center row wrap mx-2 v-on="on">
              <!-- a bunch of v-flex here that I removed for ease of reading -->
              <v-flex pl-5 md12 lg1>
                <v-dialog v-model="dialog" max-width="344">
                  <template v-slot:activator="{on}">
                    <v-btn outlined class="mr-2 blue accent-1" v-on="on">Rate</v-btn>
                  </template>
                  <RateComponent :calculator="calculator" v-bind:dialog.sync="dialog" />
                </v-dialog>
              </v-flex>
            </v-layout>
          </template>
          <span>Click RATE button on the right to rate this calculator</span>
        </v-tooltip>
      </v-card-text>
    </v-card>

So everything is under <v-tooltip> with an activator on that is bound to the v-on of <v-layout>, which includes the <v-dialog> also having an activator on bound to the v-on of <v-btn> under the very same <v-layout>. Now don't get me wrong, everything is working as it should be; the dialog is not shown on hovering the <v-layout>, nor the dialog has the tooltips displayed on hovering. But I am not sure why it works as it should. My instinct says "do not touch if it ain't broke" but my curiosity keeps poking my head πŸ˜ƒ

@mcanyucel
For nested activators it's better to name them somewhat context-wise, like

<v-tooltip>
  <template v-slot:activator="{ on: tooltip }">
    <v-layout v-on="tooltip">
      <v-dialog>
        <template v-slot:activator="{ on: dialog }">
          <v-btn v-on="dialog">Rate</v-btn>
        </template>
      </v-dialog>
    </v-layout>
  </template>
  <span>Tooltip text</span>
</v-tooltip>

As for why your example is working, consider this example:

const on = {
  click: () => {
    console.log('Outer scope')
  }
}

on.click()    // --> 'Outer scope'

const inner = () => {
  const on = {
    click: () => {
      console.log('Inner scope')
    }
  }

  on.click()
}

inner()    // --> 'Inner scope'
on.click()  // --> 'Outer scope'

(read more about scopes)

Perhaps I don’t understand how to use it. The screen is blank.
Nothing appears on the screen; no button, no press enter, no β€œclick me” just a blank screen.

So I went into my browsers webconsole; by pressing CTRL-SHIFT+J and I saw a big message in red:

vue.js:634 [Vue warn]: Error in beforeCreate hook: "Error: Vuetify is not properly initialized, see https://vuetifyjs.com/getting-started/quick-start#bootstrapping-the-vuetify-object"

Realizing what the problem was – I added

import Vuetify from 'vuetify/lib'
Vue.use(Vuetify);

to the β€œJS” section; and it worked! So I think the problem is someplace in the codepen template?

/πŸš€

Sent from Mail for Windows 10

From: Jacek Karczmarczyk
Sent: Thursday, July 18, 2019 3:21 PM
To: vuetifyjs/vuetify
Cc: elasticdotventures; Mention
Subject: Re: [vuetifyjs/vuetify] [Documentation] v-slot:activator="{ on }"(#6866)

@elasticdotventures What exactly doesn't work? https://codepen.io/anon/pen/voYNwb
β€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

That was 1.x example, here is updated to 2.0 https://codepen.io/jkarczm/pen/oNvBrar
You need to press tab twice though, because of some other bug

How can I use this v-on activator (for v-menu) for hover events ??

This is confusing as hell. To click event, this "syntax" vuetify team has choosen works out-of-box.. but when we need hover, this is complicated, when this should not be.

@aislanmaia you can use the open-on-hover prop on v-menu.

On the larger debate, I can appreciate both sides. On one hand, having an intuitive syntax is fantastic and is part of what makes working with Vue so great; on the other you have syntax that is a little cryptic but elegant and flexible enough for it to be used consistently throughout Vuetify.

Ultimately, we live in an ecosystem of abstractions where you don't have to understand everything to accomplish your goal. Stop to consider how many other dependencies you use without understanding what goes on under the hood.

Therefore, in my opinion I wouldn't change the syntax. That said, while I don't feel explaining this is necessary for consumption, for the majority of users it is at the very least a repeating pattern that Vuetify is a proponent of. I think we could help users of Vuetify by providing a short breakdown of this somewhere in the documentation.

There is no example of how to capture the events to do our own actions with this syntax.
I would guess we should do something like:

<v-tooltip bottom>
  <template #activator="{ on }">
    <v-btn icon v-on="{ ...on, click: openSettings }">
      <v-icon>mdi-settings</v-icon>
    </v-btn>
  </template>

  <span>Settings</span>
</v-tooltip>

or

<v-tooltip bottom>
  <template #activator="{ on }">
    <v-btn icon v-on="on" @click="openSettings">
      <v-icon>mdi-settings</v-icon>
    </v-btn>
  </template>

  <span>Settings</span>
</v-tooltip>

But it looks a bit weird. Also, only my second example allows setting modifiers on the click event.

My 2 cents on all the complains about is that Vue came to save us from the boilerplate that we need to write in React, Angular.... So the selling point of Vue, Vuetify was Fast to learn and fast to write an app. However with this kind of syntax it's becoming very hard to understand and take longer to add a simple tooltip. Why not make a simple tooltip directive that 90% of use use all the time. And have the Tooltip delux component that allow you to have extra cheese, french fries, and sauce...

I think it's time for me to go on my own and write my own beautiful, simple, and powerful framework! I will call it vue-ez-fy

https://github.com/vuetifyjs/vuetify/issues/9610

For dialogs and menus we still need it

BTW i think in other places (lists?) we also use v-bind="attrs", so in docs it should mentioned as well

how to export the click function to use on my v-model="dialog" and patterns components
<template v-slot:activator="{ on: { click }, value }"> <v-btn color="primary" dark class="mb-2" @click="click">Nuevo {{ value }} {{ dialog }} {{ title }}</v-btn> </template>

sure but slot="activator" won't work anymore in vuetify 2.0

so what is replaces it cus i used in my project and seems not work

<v-tooltip bottom>
  <template #activator="{ on }">
    <v-btn icon v-on="on" @click="openSettings">
      <v-icon>mdi-settings</v-icon>
    </v-btn>
  </template>

  <span>Settings</span>
</v-tooltip>

But it looks a bit weird. Also, only my second example allows setting modifiers on the click event.

I had to go through this entire thread to find the answer to my question: What is the correct syntax for getting a click event fired on a button that has a v-tooltip. Whew! And I still do not understand why I need any of that "on" stuff. This why I have so little hair left. Thanks to @pdcmoreira for figuring out that you just need to add a @ click.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dschreij picture dschreij  Β·  3Comments

smousa picture smousa  Β·  3Comments

sebastianmacias picture sebastianmacias  Β·  3Comments

SteffenDE picture SteffenDE  Β·  3Comments

cawa-93 picture cawa-93  Β·  3Comments