Home > database >  Is it possible to use v-bind on the slot of an component in vue?
Is it possible to use v-bind on the slot of an component in vue?

Time:01-21

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>

vuetify menu usage

Slot usage

  • Related