Max Coplan
Max Coplan

Reputation: 1503

Vue: wrapper for slots ignoring already-defined slots

The Vue object has a really helpful member called $attrs. What $attrs does is contain all the attributes that aren't recognized as props for the current component. A good example of $attrs is here.

I am wondering if there is an equivalent for $attrs for $scopedSlots. I am currently using an approach similar to the first suggestion from https://stackoverflow.com/a/50892881/6100005 . The issue with $scopedSlots is it also passes already-defined slots. To use that example here:


<template>
    <wrapper>
      <b-table :foo="foo" v-bind="$attrs" v-on="$listeners">
        <template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>
      </b-table>
      <slot name="mySlot"></slot>
    </wrapper>
</template>

<script>
export default {
    props: {
        // let's pretend that `b-table` has a prop `foo` that is default `false`
        foo: {
            type: boolean,
            default: true,
        }
    }
}
</script>

Now, foo will not get binded twice, thanks to the behavior of $attrs, but mySlot will be sent to both wrapper and b-table.

So how can pass I down all the slots except for the one I'm defining myself?

One idea I have is to have

computed: {
   bTableSlots() {
       Object.keys(this.$scopedSlots)
          .filter( key => key!=='mySlot' )
          .reduce( (res, key) => (res[key] = this.$scopedSlots[key], res), {} );
    }
}

And then

        <template v-for="(_, slot) of bTableSlots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>

Was wondering if there were a better way to do this. Thanks!

Upvotes: 3

Views: 767

Answers (1)

Michal Lev&#253;
Michal Lev&#253;

Reputation: 37953

So how can pass down all the slots except for the one I'm defining myself?

Why is this even a problem ? If the <b-table> doesn't have named slot with name mySlot, it will just ignore it - in the end it's just one more entry in it's $scopedSlots property the component will never access. And because the scoped slots are passed as a functions, you are not paying any additional price.

When Vue compiler sees the content of the slot (<template #slotName>), it will take the content and compile it into Array of VNodes or function returning Array of VNodes and pass it down to a child component. The fact your child component pass it down even further does not incur any additional cost. It's a reference to an existing array or a reference to an existing function (for scoped slots) and in both cases if the component further down doesn't know about the slot with exact same name, there is no additional cost except there is one more entry in $scopedSlots...

If you feel it's a problem I'm afraid filtering is only way to go - you can do it your way or by some helper array [ 'slot1', 'slot2' ] defining all existing b-table slots and filtering everything else (not better imho as you need to update component every time b-table adds new slot)....

I understand the idea and see the similarity with $attrs - there could be something as $unknownSlots on component to hold the slots not defined by the current component but there is nothing like this in Vue public API right now...

Upvotes: 1

Related Questions