vuetifyjs / vuetify

🐉 Vue Component Framework
https://vuetifyjs.com
MIT License
39.64k stars 6.95k forks source link

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

Closed YuqiaoS closed 1 year ago

YuqiaoS commented 5 years ago

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

dsseng commented 5 years ago

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

YuqiaoS commented 5 years ago

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

dsseng commented 5 years ago

@YuqiaoS see https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots

YuqiaoS commented 5 years ago

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.

dsseng commented 5 years ago

Scoped slot passes things that are passed by component

dsseng commented 5 years ago

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

jacekkarczmarczyk commented 5 years ago

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

malipetek commented 5 years ago

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

garyo commented 5 years ago

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.

yuminatwca commented 5 years ago

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 ?

MajesticPotatoe commented 5 years ago

@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.

jacekkarczmarczyk commented 5 years ago

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

Also this can be reported in vue docs repo

KaelWD commented 5 years ago

https://vuejs.org/v2/api/#v-on

garyo commented 5 years ago

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.

yuminatwca commented 5 years ago

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 ?

emjayess commented 5 years ago

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.

renetik commented 5 years ago

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

renetik commented 5 years ago

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>
DRoet commented 5 years ago

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

jacekkarczmarczyk commented 5 years ago

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

nccurry commented 5 years ago

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> component, we can optionally define an alternative \<template> for todo items, but with access to data from the child

<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

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">
farnazj commented 5 years ago

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">
tkharuk commented 5 years ago

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.

garyo commented 5 years ago

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.

tkharuk commented 5 years ago

@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

elasticdotventures commented 5 years ago

💀 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!

jacekkarczmarczyk commented 5 years ago

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

mcanyucel commented 5 years ago

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 😃

kaaposc commented 5 years ago

@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)

elasticdotventures commented 5 years ago

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.

jacekkarczmarczyk commented 5 years ago

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

aislanmaia commented 4 years ago

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.

Vercadium commented 4 years ago

@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.

pdcmoreira commented 4 years ago

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.

mtermoul commented 4 years ago

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

jacekkarczmarczyk commented 4 years ago

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

LAxBANDA commented 4 years ago

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>

Asinging commented 4 years ago

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

bradlymathews commented 4 years ago
<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.

Dinuz commented 3 years ago

@pdcmoreira that's right. I did exactly that, but instead of using a single click I used the whole $listeners in a wrapper fashion way. it results in something like the following:

<template v-slot:activator="{ on }">
  <my-comp v-on="{...on, ...$listeners}" />
</template>

It makes sense if you think that are both objects:)) The issue raise when you forget that "on" is an object.

pdcmoreira commented 3 years ago

@Dinuz sure, but that's only if you want to pass all the listeners from parent to child.

Dan88Hus commented 3 years ago

this docs explanation can be short answer for all question about slots and its details :

robinzimmermann commented 2 years ago

I'm using typescript in vscode with vue-3 and vuetify next.

The sample code for a dialog on the vuetify site is:

<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-dialog>
  </div>
</template>

But I kept getting a ts compile error:

Property 'on' does not exist on type '{ isActive: boolean; props: Record<string, any>; }'.
Property 'attrs' does not exist on type '{ isActive: boolean; props: Record<string, any>; }'.
image

Thanks to some of the posts above, I was able to modify the code and get it working. The key line is to change:

<template v-slot:activator="{ on, attrs }">

to:

<template v-slot:activator="{ isActive: on, props: attrs }">

I'm just paying it forward in case this helps anyone else.

Thank you folks on this thread!

lhuangjs commented 2 years ago

自动回复:我已收到邮件,谢谢!!

KaelWD commented 2 years ago

@robinzimmermann Look at the examples on https://next.vuetifyjs.com/en/components/dialogs/

robinzimmermann commented 2 years ago

Thanks @KaelWD!

And thank you for not also throwing in "RTFM!"

danieldanielecki commented 1 year ago

Is this still relevant? It seems to be all examples have been replaced with props instead, and v-on is no longer referred.

KaelWD commented 1 year ago

https://vuetifyjs.com/en/components/overlays/#activator-slot