Bruno Francisco
Bruno Francisco

Reputation: 4240

Using VMenu from vuetify with render function (scoped slot)

I'm trying to use Vuetify's VMenu component and I would like that when a user clicks the button the VMenu shows up. As far as the docs goes it says we should add a scoped slot. Doing with a normal template it works but when I switch to a render function approach it never renders the button.

I have been following the Vue's docs and ended up with:

h(VMenu, { props: { value: isMenuOpen.value } }, [
            h(
              "template",
              {
                scopedSlots: {
                  activator: ({ on, attrs }) => {
                     debugger; // it never reaches this debugger
                     return h(VButton, { on, attrs }, 'click me');
                  }
                },
              },
              []
            ),
            h(VList, [h(VListItem, [h(VListItemTitle, ["Logout"])])]),
          ]),

I have tried using a non-arrow function as well:

scopedSlots: { activator: function({ on, attrs }) {  return h('div', 'click me');  } }

and return a simple h('div', 'click me') in both non-arrow function and arrow function and nothing seems to work.

How can I pass the scoped slot activator to VMenu component?

Upvotes: 0

Views: 760

Answers (2)

tony19
tony19

Reputation: 138656

Scoped slots are passed through the scopedSlots property of createElement's 2nd argument in the form of { name: props => VNode | Array<VNode> }. In your case, scopedSlots should have two entries: one for activator, and another for default:

import { VMenu, VList, VListItem, VBtn } from 'vuetify/lib'

export default {
  render(h) {
    return h(VMenu, {
      scopedSlots: {
        activator: props => h(VBtn, props, 'Open'),
        default: () => h(VList, [
          h(VListItem, 'item 1'),
          h(VListItem, 'item 2'),
          h(VListItem, 'item 3'),
        ]),
      },
    })
  }
}

which is equivalent to this template:

<template>
  <v-menu>
    <template v-slot:activator="{ on, attrs }">
      <v-btn v-bind="attrs" v-on="on">Open</v-btn>
    </template>
    <v-list>
      <v-list-item>item 1</v-list-item>
      <v-list-item>item 2</v-list-item>
      <v-list-item>item 3</v-list-item>
    </v-list>
  </v-menu>
</template>

demo

Upvotes: 2

Bruno Francisco
Bruno Francisco

Reputation: 4240

I wasn't able to fully understand the problem described in my question. This is an answer not to answer the fully original question but to guide future users that may come to this question.

Instead of using a scoped slot I have used the value prop in combination with attach prop. This solution in the end ended up working without no problem.

button(
            {
              attrs: { "data-account-setting": true },
              props: { plain: true, rounded: true, icon: true },
              on: { click: onOpenMenuClick },
            },
            [h(VIcon, ["mdi-account-outline"])]
          ),
          h(
            VMenu,
            {
              props: {
                value: isMenuOpen.value,
                // waiting on answer on SO
                // @see https://stackoverflow.com/questions/67405594/using-vmenu-from-vuetify-with-render-function-scoped-slot
                attach: "[data-account-setting]",
                minWidth: "300px",
                left: true,
                offsetY: true,
                closeOnContentClick: false,
                rounded: true,
              },
              on: {
                input: (value: boolean) => {
                  isMenuOpen.value = value;
                },
              },
            },
            [
              h(VList, { props: { dense: true } }, [
                h(VListItem, { props: { to: { name: "logout" } } }, [
                  h(VListItemTitle, { attrs: { 'data-cy-logout': true } }, ["Logout"]),
                ]),
              ]),
            ]
          ),

Upvotes: 0

Related Questions