3

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!

tony19
  • 125,647
  • 18
  • 229
  • 307
Max Coplan
  • 1,111
  • 13
  • 27

1 Answers1

1

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...

Michal Levý
  • 33,064
  • 4
  • 68
  • 86
  • I'm more worried about performance than naming collisions. Are you saying that since `$scopedSlots` is an object with the `name`s as keys, if I add an unused entry to it it will have minimal performance impact? If so that's an acceptable solution – Max Coplan Oct 05 '20 at 18:31
  • If you add "unused" slot, the price will be compilation time (while building) and memory + time needed by JS engine to parse the render function for the slot passed. But if you pass "mySlot", the function created will be used by your component (no waste). The fact the function is **also** passed into 'b-table' `$scopedSlots` is irrelevant ...its the same function – Michal Levý Oct 05 '20 at 18:41
  • @MaxCoplan Extended my answer with focus on performance. Hope it is clear now... – Michal Levý Oct 05 '20 at 19:05