I came across an Vuetify example for v-dialog component which has the scoped slot called activator, defined as follows:
<!-- 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)
}
})