Search code examples
javascriptarraysvue.jsvue-componentslot

Build a dynamic number of Vue slots within a Render function


I'm trying to build a custom component from a render function.

This component being rendered accepts any number of slots. In the example below there are three available slots (named element_1, element_2, element_3).

The below Array.reduce() is meant to be equivalent to:

scopedSlots: {
  "element_1": () => createElement('div', 'hello world'),
  "element_2": () => createElement('div', 'hello world'),
  "element_3": () => createElement('div', 'hello world'),
}

This is a slimmed down example with Array.reduce():

const records = [
  {
    "index": 1,
  },
  {
    "index": 2,
  },
  {
    "index": 3,
  }
]

render: function (createElement) {
  return createElement("replicator-component", {
    attrs: { elements: records.length},

    scopedSlots: records.reduce((a,x) => ({...a, 
      ['element_' + x.index]: 
      () => { createElement( 'div', 'hello world') }}), {})
  });
},

However nothing renders and there's no errors to guide me. Any ideas?


Solution

  • The difference is that in your reduce, you are creating the functions as

    () => { createElement( 'div', 'hello world') }
    

    while in your hardcoded version (and also in the forEach loop in @Boussadjra's anwer) they are created as

    () => createElement('div', 'hello world')
    

    which actually return the created element. It's nothing to do with the use of reduce, which is fine.

    const ReplicatorComponent = {
      template: `<div>
        <h1>replicator-component</h1>
        <slot name='element_1'></slot>
        <slot name='element_2'></slot>
        <slot name='element_3'></slot>
      </div>`
    };
    
    const records = [
      { "index": 1 },
      { "index": 2 },
      { "index": 3 },
    ];
    
    Vue.component('my-component', {
      render: function(createElement) {
        return createElement(ReplicatorComponent, {
          attrs: {
            elements: records.length
          },
          scopedSlots: records.reduce((a,x) => ({
            ...a, 
            ['element_' + x.index]: () => 
              createElement( 'div', 'hello world')
           }), {})
        });
      },
    });
    
    new Vue({
      el: '#app',
      data: () => ({})
    });
    <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
    
    <div id="app">
      <my-component></my-component>
    </div>