I want to create a mini profile in form of a menu by using Vuetify.js that can be called by different objects in my SPA so I thought it would be a good idea to create a component for it.
The component looks like this:
MiniProfileMenu.vue
<template>
<v-menu
v-model="menu"
:close-on-content-click="false"
location="end"
>
<template v-slot:activator="{ activator }">
<slot v-bind="activator"/>
</template>
<v-card>
<v-row>
<v-col cols="auto" style="width: 64px">
Avatar
</v-col>
<v-col>
<h4>User</h4>
<p >User Desc</p>
</v-col>
</v-row>
</v-card>
</v-menu>
</template>
<script>
export default {
name: "MiniProfileMenu",
data() {
return {
menu: false,
activator: null,
};
},
methods: {
open(user, activator) {
this.activator = activator;
this.menu = true;
},
close() {
this.menu = false;
},
},
}
</script>
<style scoped>
.profile-card {
width: 100%;
}
</style>
And this is the case I would like to use it:
DashboardCommunityPage.vue
<template>
<v-container>
<v-row>
<v-spacer />
<v-col cols="auto">
<mini-profile-menu ref="miniProfile">
<div
@click="openMiniProfile(user_uuid)"
>
Click me!
</div>
</mini-profile-menu>
</v-col>
<v-spacer />
</v-row>
</v-container>
</template>
<script>
import MiniProfileMenu from "#/components/MiniProfileMenu.vue";
export default {
name: "DashboardCommunityPage",
components: {MiniProfileMenu},
methods: {
openMiniProfile(uuid) {
this.$refs.miniProfile(uuid);
}
}
};
</script>
My problem is now, that I get the error
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'key')
from the v-bind
on the <slot />
in my MiniProfileMenu component. I would like to bind it to the div that is in the slot of the MiniProfileMenu so the menu appears next to it on click. Is it possible to do that in another way?
Thanks in advance
CodePudding user response:
You are not binding the slot's props correctly:
<v-menu>
<template v-slot:activator="{ activator }">
...
</template>
According to the docs, the activator
slot has these props:
{ isActive: boolean; props: Record<string, any> }
So your destructured variable activator
will always be undefined.
You probably want to do:
<v-menu>
<template v-slot:activator="{ props, isActive }">
<slot :props="props" :isActive="isActive" />
</template>
or
<v-menu>
<template v-slot:activator="slotProps">
<slot v-bind="slotProps" />
</template>
These two are functionally equivalent, in the first version, you take the slot props apart manually and passes them on one by one, while in the second version, v-bind
takes them apart for you. When using the slot of your component, the individual props will be bundled again into a new slot props object, you can use it exactly as above:
<mini-profile-menu v-slot="{props, isActive}">
<div
@click="props['update:modelValue'](true)"
>
Click me!
</div>
</mini-profile-menu>
CodePudding user response:
In the Vuetify menu
component there is no props called activator
.
Instead you need to bind the props
prop to where you want the menu to open on click.
And in your menu component you can expose this props to the parent component by binding this to the slot
.
<!-- my-menu.vue -->
<template>
<v-menu>
<template v-slot:activator="{ props }">
<slot v-bind="props" />
</template>
<v-card>
<!-- Content showed when opened -->
</v-card>
</v-menu>
</template>
<!-- parent component -->
<template>
<MyMenu v-slot="props"> <!-- We can access the props exposed from the child component -->
<v-btn v-bind="props">
Click me!
</v-btn>
</MyMenu>
</template>