4

is it possible to set attributes on a slot and the element from the parent gets these attributes?

Parent

<vDropdown>
  <button slot="button">new button</button>
  <ul>content</ul>
</vDropdown>

Dropdown.vue

<div>
  <slot name="button" aria-haspopup="true">
    //fallback
    <button aria-haspopup="true">Default Button</button>
  </slot>
  <div id="name" :aria-expanded="expanded">
    <slot />
  </div>
</div>

the output for the button is without any attributes...

<div>
  <button>new button</button>
  <div id="myDropdown" aria-expanded="false">
    <ul>content</ul>
  </div>
</div>
Gregor Voinov
  • 2,203
  • 7
  • 34
  • 52

1 Answers1

8

Use Scoped Slots

Step 1. In the parent, update the old deprecated slot targeting syntax slot="button" to the v-slot directive:

Parent.vue

...
<template v-slot:button>                   ✅
  <button>new button</button>
</template>
...
<button slot="button">new button</button>  ❌

How to target a slot in Vue 2.6.0+

Step 2. Next, understand that any attribute bindings you add to a <slot> tag will become available to any slotted content placed there (these are called "slot props"):

Dropdown.vue

<slot name="button" :aria-haspopup="true">

Step 3. Vue automatically creates an object containing every binding from Step 2, and passes that to the v-slot expression, i.e. slotProps below. You can then use the special v-bind="" syntax to spread all those bindings onto the button:

Parent.vue updated

<template v-slot:button="slotProps">
  <button v-bind="slotProps">new button</button>
</template>

Here's a demo, but sadly it requires a hack using two hyphens when you do this with a kebab-case attribute. I'll plan to submit an issue for this in the Vue GitHub repo.

Vue.component('dropdown', {
  template: `
  <div>
    <slot name="button" aria--haspopup="true">
      <button aria-haspopup="true">Default Button</button>
    </slot>
    <div id="name" :aria-expanded="expanded">
      <slot />
    </div>
  </div>`,
  data() {
    return {
      expanded: true
    }
  }
});

new Vue({
  el: "#app",
});
.aria-haspopup {
  background: orange;
}
<div id="app">
  <dropdown>
    <template v-slot:button="slotProps">
      <button v-bind="slotProps">new button</button>
    </template>
    <ul>content</ul>
  </dropdown>
</div>

<script src="https://unpkg.com/vue"></script>
tony19
  • 125,647
  • 18
  • 229
  • 307
Dan
  • 59,490
  • 13
  • 101
  • 110
  • Hi, thank you for you detailed answer, but if you inspect your code snippet the aria attribute 'aria-haspopup' is missing. My intention was just to place some attributes on the slot, without adding more complexity. So I think I will just parse the $el for the first button and place the attributes via js. – Gregor Voinov Feb 14 '21 at 11:18
  • I updated the demo, I had misread the question as a class rather than an attribute. It works great... except for attributes with a hyphen, so I submitted an issue to the Vue repo. I'll leave the answer up in hopes that they fix it (and I showed a workaround in the demo). – Dan Feb 14 '21 at 12:34