2
votes

Hi everybody, please pardon my english :-)

I have a Vue component that can take dynamic slots (the names of the slots will depend on a props).

I use it on several places and some of the slots are always present.

To avoid redundancy, I'm looking for a way to create a component that "wrap" the final component to allow me to define only the additionals slots.

If there is an "obvious" way to achieve it, I may have missed it :-)

Code example

Without a "wrap component"

<b-table
  show-empty
  small
  hover

  [...some others and always present props...]

  :items="aDataVarThatWillChangeBasedOnTheContext"

  [...some others and uniq props...]
>
  <template slot="same-1">
   A slot that will always be present with the same content (for example, a checkbox in the first column)
  </template>

  <template slot="same-2">
   A slot that will always be present with the same content (for example, some action buttons in the last column)
  </template>

  [...some others and always present slots...]

  <template slot="not-the-same">
   A slot that is only used in this context (for example, a duration based on a row timestamp and a timestamp picked by the user)
  </template>

  [...some others and uniq slots...]
</b-table>

With a "wrap component"

<my-b-table
  :items="aDataVarThatWillChangeBasedOnTheContext"
>
  <template slot="not-the-same">
   A slot that is only used in this context (for example, a duration based on a row timestamp and a timestamp picked by the user)
  </template>
</my-b-table>

Note: The dynamic slot name is not predictible. If I suddenly need a "foo" column, I should be able to pass a "foo" slot (and a "HEAD_foo" slot, in my case)

Some researches

I read here that:

They’re (the functionnal components) also very useful as wrapper components. For example, when you need to:

  • Programmatically choose one of several other components to delegate to
  • Manipulate children, props, or data before passing them on to a child component

And "Manipulate children, props, or data before passing them on to a child component" seems to be exactly what I need.

I looked on render function but a lot of things seems to be not implemented, like the v-model, and I have difficulties to figure out how to pass dynamic slots...

Thank you in advance for your(s) answer(s) !

up: At the 07.03.2018 I still dont have any idea about how to solve this case

2

2 Answers

2
votes

Found the answer that was somehow unclear to me that month ago.

("Dynamic" means here "not explicitely declared by the component, but gived by the parent")

Wrapper component

Props and scoped slots can be gived dynamically by the options object of createElement function.

"Simple" Slots can be gived dynamically by the childs array of createElement function.

Wrapped component

Props can't be dynamic unless the component is functional.

Slots can always be retrieved dynamically.

Scoped slots can be retrieved only if the component isn't functional.

Conclusion

It's not possible to have dynamics props and scoped slots at the same time...

But it's possible to declare all the needed props and then to use a "non-functionnal" component as wrapper and as wrapped.


How to

Retrieve from non-functional component

var component = Vue.component('component-name', {
  props: ['name', 'of', 'the', 'props'],

  // [...]

  aMethod: function () {
    this._props // all the declared props
    this.$slots // all the slots
    this.$scopedSlots // all the scoped slots
  }
});

Retrieve from functional component

var component = Vue.component('component-name', {
  functional: true,

  render: function (createElement, context) {
    context.props // all the props
    context.children // all the slots as an array
    context.slots() // all the slots as an object
  }
});

Give to child component

var component = Vue.component('component-name', {
  render: function (createElement) {
    return createElement(
      childComponent,
      {
        props: propsToGive,
        scopedSlots: scopedSlotsToGive
      },
      [
        // non-scoped slots to give
        createElement('slot-tag-name', {slot: 'slot-name'})
      ]
    );
  }
});

References

https://vuejs.org/v2/guide/render-function.html

https://vuejs.org/v2/guide/render-function.html#createElement-Arguments

https://vuejs.org/v2/guide/render-function.html#Functional-Components


Sandbox

https://jsfiddle.net/5umk7p52/

0
votes

Just make a regular component out of your customized <b-table>.

You'll need to define an items prop for your component to pass as the items for the <b-table> component.

And, to define a slot for your component, you'll need to use the <slot> tag, specifying the name using the name attribute.

If you'd like to make one of the slots from the <b-table> component accessible in the <my-b-table> component, simply pass a <slot> tag as the content of the slot in your custom component.

It would look something like this:

Vue.component('my-b-table', {
  template: `
    <b-table
      show-empty
      small
      hover
      :items="items"  
    >
      <template slot="same-1">
        Content to pass to the b-table's slot
      </template>

      <slot name="not-the-same">
        A slot that is only used in this context
      </slot>

      <template slot="last_edit">
        <slot name="last_edit">
          A slot to pass content to the b-table component's slot
        </slot>
      </template>
    </b-table>
  `,
  props: { items: Array },
});