Vuejs nested slots: how to pass slot to grandchild

2019-05-31 04:45发布

I use different vuetify components, for example v-menu. It has a template like this:

<v-menu>
  <a slot="activator">menu</a>
  <v-list>
    <v-list-tile>Menu Entry 1</v-list-tile>
    <v-list-tile>Menu Entry 2</v-list-tile>
  </v-list>
</v-menu>

Suppose I want to add another wrapper around it. That is my special menu component that has some predefined menu options. And I want it to has an activator slot as well. And the last should be somehow assigned to the original v-menu activator slot. Is it possible?

Example:

// index.vue: 
<template>
  <my-special-menu>
    <button>My special menu trigger</button>
  </my-special-menu>
</template>

// MySpecialMenu.vue
<template>
  <v-menu>
    <slot slot="activator"/> <-- I don't know how to write this line
    <v-list>...</v-list>
  </v-menu>
</template>

<slot slot="activator"> is an incorrect equation. The goal is to pull the content from the parent (that is <button>..</button> in the example), and use it as slot="activator" in v-menu.

I can write it like this:

<v-menu>
  <a slot="activator"><slot/></a>
  ...
</v-menu>

But this case the result template will be:

<div class="v-menu__activator">
  <a>
    <button>My special menu trigger</button>
  </a>
</div>

That's not exactly what I want. Is it possible to get rid off <a> wrapper here?

Update: We can use a construction like <template slot="activator"><slot name="activator"/></template> to throw some slot to a grand child. But what if we have multiple slots and we want to proxy them all? That's like inheritAttrs and v-bind="$attrs" for slots. Is it currently possible?

For example, there's <v-autocomplete> component in vuetify that has append, prepend, label, no-data, progress, item, selection etc slots. I write some wrapper component around this, it currently looks like:

<template>
  <v-autocomplete ..>
    <template slot="append"><slot name="append"/></template>
    <template slot="prepend"><slot name="prepend"/></template>
    <template slot="label"><slot name="label"/></template>
    ...
    <template slot="item" slot-scope="props"><slot name="item" v-bind="props"/></template>
  </v-autocomplete>
</template>

Is it possible to avoid all slots enumeration here?

1条回答
迷人小祖宗
2楼-- · 2019-05-31 05:15

If you put the slot attribute on a html element, that html element is passed to the child component to fill the slot with that name. If you don't want to pass along a html element, you can use slot on a template tag within your component. A template tag groups elements, but does not render to a html element, which is perfect here. You can use template tags also for other things, such as to group elements in a v-if for example, or to repeat multiple elements with a v-for.

// App.vue
<template>
  <div id="app">
    <test>
      <template slot="activator">
        Click <b>me</b>!
      </template>
    </test>
  </div>
</template>
// Test.vue
<template>
  <div class="wrapper">
    <grand-child>
      <template slot="activator">
        <slot name="activator"></slot>
      </template>
    </grand-child>

    This is some text
  </div>
</template>
// GrandChild.vue
<template>
  <div>
    <a href="#" @click="toggle = !toggle">
      <slot name="activator">Default</slot>
    </a>

    <div v-if="toggle">This appears and disappears</div>
  </div>
</template>

Edit Vue Template

Edit: If you want to do this for arbitrary slots, this is also possible. this.$slots contains the slots and their content, so with something like the following, you can pass the slot content to a slot with the same name:

<grand-child>
  <template v-for="(_, slot) in $slots">
    <template :slot="slot">
      <slot :name="slot"></slot>
    </template>
  </template>
</grand-child>

Edit Vue Template

查看更多
登录 后发表回答