🖥️

Vuetifyのactivatorをネストして使いたい

2021/11/17に公開

お困りごと

Vuetifyで以下のようなボタンコンポーネントを作りたい。

  • マウスオーバー時にTooltip表示
  • クリック時にメニュー表示

menu_tooltip

が、単純に公式のサンプルをそのまま持ってきて組み合わせても上手くいかない。

エラーが出るコード
<template>
  <div class="text-center">
    <v-menu offset-y>
      <template #activator="{ on }"> <!-- v-menuのactivator -->
        <v-tooltip bottom>
          <template #activator="{ on, attrs }"> <!-- v-tooltipのactivator -->
            <v-btn
              color="primary"
              dark
              v-bind="attrs"
              v-on="{ on }"
            > <!-- ここのonはv-tooltipのonが参照される -->
              Dropdown
            </v-btn>
          </template>
          <span>Tooltip</span>
        </v-tooltip>
      </template>

      <v-list>
        <v-list-item v-for="(item, index) in items" :key="index">
          <v-list-item-title>{{ item.title }}</v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  </div>
</template>

<script>
export default {
  data: () => ({
    items: [
      { title: "Click Me" },
      { title: "Click Me" },
      { title: "Click Me" },
      { title: "Click Me 2" },
    ],
  }),
};
</script>

v-tooltipのactivatorだけでなく、v-menuのactivatorも動くようにしたいです。

解決策

#activator="{ on }"って書かない。

OKなコード
<template>
  <div class="text-center">
    <v-menu offset-y>
      <template #activator="menuActivator"> <!-- v-menuのactivator -->
        <v-tooltip bottom>
          <template #activator="tooltipActivator"> <!-- v-tooltipのactivator -->
            <v-btn
              color="primary"
              dark
              v-bind="menuActivator.attrs"
              v-on="{ ...menuActivator.on, ...tooltipActivator.on }"
            > <!-- 〇〇Activator.on のような形にする -->
              Dropdown
            </v-btn>
          </template>
          <span>Tooltip</span>
        </v-tooltip>
      </template>

      <v-list>
        <v-list-item v-for="(item, index) in items" :key="index">
          <v-list-item-title>{{ item.title }}</v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  </div>
</template>

<script>
export default {
  data: () => ({
    items: [
      { title: "Click Me" },
      { title: "Click Me" },
      { title: "Click Me" },
      { title: "Click Me 2" },
    ],
  }),
};
</script>

なんでこんな書き方になるのか

公式ドキュメントでv-menuのslotについての記述を見てみましょう。
https://vuetifyjs.com/ja/api/v-menu/#slots

activatorありますね。

{
  attrs: { role: string, aria-haspopup: boolean, aria-expanded: string },
  on: { [eventName]: eventHandler },
  value: boolean
}

ここに見覚えのあるattrsonがありますね。
#activator="{ on, attrs }"と書くと、このactivatorが分割代入されます。
VuetifyやVueのサンプルを見ていると分割代入されているものが多そうですが、{}を付けないことで分割せずに取得できるようです。

あまりネストが深くなるような使い方はそもそもしたくないですが、深くなるとどのactivatorか判別しにくくなると思うので、適切な変数名を付けて少しでも可読性が上がるようにしたいですね。

GitHubで編集を提案

Discussion