Gregor Voinov
Gregor Voinov

Reputation: 2263

Vue.js how to use attributes on slots

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>

Upvotes: 5

Views: 8991

Answers (1)

Dan
Dan

Reputation: 63119

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>

Upvotes: 9

Related Questions