Reputation: 339
First things first I use the <script setup>
API.
In Vuetify components like v-menu
have a slot called activator
where you can put a custom element as your dropdown "button" and bind the onClick
listener by v-binding the props passed to that slot from inside the v-menu
component like so:
<v-menu>
<template #activator="{ props: activatorProps }">
<v-btn v-bind="activatorProps">Expand me</v-btn>
</template>
<v-list>
<v-list-item>1</v-list-item>
<v-list-item>1</v-list-item>
<v-list-item>1</v-list-item>
</v-list>
</v-menu>
How can I create such component with a slot that has @click
binded to it from inside the component so I can "bind" the @click
event to the element I pass to that slot?
Here is my in-progress dropdown component:
<template>
<div :class="$style['dropdown']">
<slot name="activator" @click="expand">
</slot>
<div :class="$style['dropdown__content']">
<a href="">Item1</a>
<a href="">Item2</a>
<a href="">Item3</a>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue"
import { ref } from "vue"
const props = defineProps<{
backgroundColor?: string
}>()
const expanded = ref(false)
const menuDisplayCssProp = computed(() => expanded.value ? "block" : "none")
const expand = () => {
expanded.value = !expanded.value
}
</script>
<style module lang="scss">
.dropdown {
button {
cursor: pointer;
}
a {
display: block;
text-decoration: none;
}
&__content {
display: v-bind(menuDisplayCssProp);
position: absolute;
background-color: v-bind(backgroundColor);
}
}
</style>
I want to be able to use it the same way as Vuetify's v-menu
:
<AppDropdown>
<template #activator="{ props: activatorProps }">
<button type="button" v-bind="activatorProps">Expand me</button>
</template>
</AppDropdown>
Upvotes: 3
Views: 536
Reputation: 8423
props.onClick
, if you want to pass many properties)Pass a reactive props object to v-bind
where you embed the @click
event into a property named onClick
. See the example.
const { createApp, reactive } = Vue
const MyComponent = {
template: `
<div>
<slot name="activator" :activatorProps="activatorProps"></slot>
</div>
`,
setup() {
const activatorProps = reactive({
onClick: () => alert('expand')
})
return {
activatorProps
}
}
}
const app = createApp({
template: `
<MyComponent>
<template #activator="{ activatorProps }">
<button type="button" v-bind="activatorProps">Expand me</button>
</template>
</MyComponent>
`,
})
app.component('MyComponent', MyComponent)
app.mount('#app')
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>
<div id="app"></div>
Alternatively, you can also pass just the onClick
function under its original name, expand, as mentioned in the question.
const { createApp, reactive } = Vue
const MyComponent = {
template: `
<div>
<slot name="activator" :expand="expand"></slot>
</div>
`,
setup() {
const expand = () => alert('expand')
return {
expand
}
}
}
const app = createApp({
template: `
<MyComponent>
<template #activator="{ expand }">
<button type="button" @click="expand">Expand me</button>
</template>
</MyComponent>
`,
})
app.component('MyComponent', MyComponent)
app.mount('#app')
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>
<div id="app"></div>
For my StackOverflow runnable example, I had to use CDN format. Feel free to break down the components into separate files and use the <template>
HTML tag and <script setup>
for Composition API, of course.
Upvotes: 2
Reputation: 361
AppDropdown.vue:
<template>
<div>
<slot name="activator" :props="activatorProps" :expanded="expanded"></slot>
<slot name="content" v-if="expanded"></slot>
</div>
</template>
<script setup>
import { ref } from 'vue'
const expanded = ref(false)
const activatorProps = ref({
onclick: () => {
expanded.value = !expanded.value
},
})
</script>
Usage:
<template>
<AppDropdown>
<template #activator="{ props, expanded }">
<button v-bind="props">{{ expanded ? 'Hide' : 'Show' }}</button>
</template>
<template #content>
<div>Hi!</div>
</template>
</AppDropdown>
</template>
<script setup>
import AppDropdown from './AppDropdown.vue'
</script>
Upvotes: 2