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?
The v-on:click directive is a Vue. js directive used to add a click event listener to an element. First, we will create a div element with id as app and let's apply the v-on:click directive to a element. Further, we can execute a function when click even occurs. Syntax: v-on:click="function"
Vue v-model is a directive that creates a two-way data binding between a value in our template and a value in our data properties. A common use case for using v-model is when designing forms and inputs. We can use it to have our DOM input elements be able to modify the data in our Vue instance.
If you – for whatever reason – want to only output something one time, then you can use the v-once directive. This directive ensures that an element is only rendered once, and when Vue. js re-renders the page, the element and all of its children will be considered as static content and thereby skipped.
v-bind is a one-way binding. v-model is used for binding form elements like inputs, radio buttons, textarea, checkboxes. It is used for binding data, attributes, expressions, class, styles. V-model is input value sensitive. It is also used to pass props to child components.
<!-- object syntax (2.4.0+) --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>]
So basically @click="..."
equals v-on:click="..."
equals v-on="{click:...}"
genActivator () { const node = getSlot(this, 'activator', Object.assign(this.getValueProxy(), { on: this.genActivatorListeners(), attrs: this.genActivatorAttributes(), })) || [] this.activatorNode = node return node }
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 />
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 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
.
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)
}
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With