38

I came across a Vuetify example for the v-dialog component which has the scoped slot called activator defined as follows:

<template v-slot:activator="{ on }">
  <v-btn
    color="red lighten-2"
    dark
    v-on="on"
  >
    Click Me
  </v-btn>
</template>

I understand the purpose of scoped slots from VueJS docs and the concept of destructuring slot props but I don't understand what the meaning of v-on="on" is in this example. In particular what it means when the event is not specified with the v-on directive?

The VueJS docs on v-on only show its usage in combination with an event name explicitly specified (eg. v-on:click="...") but there is no explanation of just using it as v-on="...".

Can someone explain this syntax and its usage in the Vuetify example?

7
  • 2
    vuejs.org/v2/api/#v-on : <!-- object syntax (2.4.0+) --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button> Commented Aug 16, 2019 at 11:57
  • @Estradiaz please post that as an answer Commented Aug 16, 2019 at 12:11
  • 2
    @adiga That's the answer. However, the OP is asking to explain how it is used in the mentioned example, where on is not defined anywhere other than in the activator and is used in v-on. Commented Aug 16, 2019 at 12:12
  • @briosheje on is probably a property of data of that component? (Not that familiar with vue btw) Commented Aug 16, 2019 at 12:15
  • @adiga I'm not familiar either, but I don't think a simple link to the documentation actually helps, since if you take a look at the example it's still unclear how "on" works in that context. Commented Aug 16, 2019 at 12:28

1 Answer 1

50

TLDR:

basic usage

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

So basically @click="..." equals v-on:click="..." equals v-on="{click:...}"


TLDR:

vuetify implementation:

genActivator () {
      const node = getSlot(this, 'activator', Object.assign(this.getValueProxy(), {
        on: this.genActivatorListeners(),
        attrs: this.genActivatorAttributes(),
      })) || []

      this.activatorNode = node

      return node
    }

Some insight:


It is useful if you want to abstract components and pass down multiple listeners at once instead of writing multiple lines of assignments.


Consider a component:

export default {

  data() {
    return {
      on: {
        click: console.log,
        contextmenu: console.log
      },
      value: "any key value pair"
    }
  }
}
<template>
  <div>
    <slot name="activator" :on="on" :otherSlotPropName="value" >
      <defaultComponent v-on="on" />
    </slot>
  </div>
</template>

Given the component above, you can access the slot properties and pass them into your custom component:

<ExampleComponent>
  <template v-slot:activator="{ on, otherSlotPropName }">
    <v-btn
      color="red lighten-2"
      dark
      v-on="on"
    >
      Click Me
    </v-btn>
  </template>
 <ExampleComponent />

Somethimes its easier to see it in plain javascript:

Comparing the component from above - with render function instead of template:

export default {

  data() {
    return {
      on: {
        click: console.log,
        contextmenu: console.log
      },
      value: "any key value pair"
    }
  },
  render(h){

    return h('div', [
      this.$scopedSlots.activator &&
      this.$scopedSlots.activator({
        on: this.on,
        otherSlotPropName: this.value
      })
      || h('defaultComponent', {
        listeners: this.on
      }
    ]
  }
}

In the source:

In case of a blank v-on="eventsObject" the method bindObjectListener will be called resulting in the assignment of the events to data.on.

This happens in the createComponent scope.

Finaly the listeners are passed as VNodeComponentOptions and updated by updateListeners.


Where Vue extends - the Vuetify implementation inspected:

When taking into account that one can join and extend vue instances, one can convince himself that any component can be reduced to a more atomic version.

This is what vuetify utilizes in the e.g. v-dialog component by creating a activator mixin.

For now one can trace down the content of on mounted by the activatable:

const simplyfiedActivable = {

  mounted(){
    this.activatorElement = this.getActivator()
  },
  watch{
    activatorElement(){
      // if is el?
      this.addActivatorEvents()
    }
  },
  methods: {
    addActivatorEvents(){
      this.listeners = this.genActivatorListeners()
    },
    genActivatorListeners(){
      return {
        click: ...,
        mouseenter: ...,
        mouseleave: ...,
      }
    },
genActivator () {
      const node = getSlot(this, 'activator', Object.assign(this.getValueProxy(), {
        on: this.genActivatorListeners(),
        attrs: this.genActivatorAttributes(),
      })) || []

      this.activatorNode = node

      return node
    },
  }
}

With above snippet all there is left is to implement this into the actual component:

// vuetify usage/implemention of mixins 
const baseMixins = mixins(
  Activatable,
  ...other
)

const sympliefiedDialog = baseMixins.extend({
  ...options,
  render(h){
    
    const children = []
    children.push(this.genActivator())
    return h(root, ...options, children)
  }
})

Sign up to request clarification or add additional context in comments.

3 Comments

i still don't get it :/
Basically, the pattern is to create a variable called on which is an object that holds all of the various event handlers. So instead of having to repeat v-on:click="foo()" and v-on:contextmenu="bar()" on a bunch of different components, you just say v-on="on" and they're all specified by what's in the on object. I guess it's a pattern/convention to call it that, but it's tough to find the definition of a two-letter variable name in a large application :/
I think they removed it, and now it's way easier to deal with v-tooltip with props, instead of v-on, vuetifyjs.com/en/components/tooltips

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.