Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vuejs nested slots: how to pass slot to grandchild

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?

like image 515
Kasheftin Avatar asked Nov 22 '18 12:11

Kasheftin


People also ask

How do I use the < slot> element in react grandchild?

We need it to take the content from Parent and provide it to the Grandchild component, so we're connecting two different slots together here: Remember, the <slot /> element renders out content that's passed to the component as a slot. So we're taking that content from Parent and then rendering it inside of the slot of Grandchild.

What is the use of slot attribute in HTML?

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.

What happens if no slot is provided in a list?

If no slot is provided, we default to what's inside of the <slot> element, and render list [0] just like before. But if we do provide a slot, it will render it out and pass the list item to the parent through the slot scope. The recursive case here is similar.

Is there a <V-autocomplete> component in vuetify?

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:


Video Answer


2 Answers

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

For completeness sake, scoped slots can be accessed through $scopedSlots and be propagated like so:

<grand-child>
  <template v-for="(_, slot) in $scopedSlots" v-slot:[slot]="props">
    <slot :name="slot" v-bind="props" />
  </template>
</grand-child>

source and comment

like image 134
Sumurai8 Avatar answered Sep 21 '22 23:09

Sumurai8


I had EsLint errors because of the depreciated :slot and $scopedSlots attributes.

So I combined both of @Sumurai8 answers like this and it works great:

<template v-for="(_, slot) in $slots" v-slot:[slot]>
  <slot :name="slot"></slot>
</template>
like image 25
TinyTiger Avatar answered Sep 22 '22 23:09

TinyTiger