2
votes

I'm trying to pass slot into the slot but child component which requires passed down slot doesn't see it.

In the child (TheTable) I have table component from Core UI for Vue (CDataTable) which requires certain slots which I want to pass from a parent:

  <CDataTable
      class="overflow-auto"
      :items="this.items"
      :fields="fieldData"
      :sorter="{ external: true, resetable: true }"
      :sorter-value="this.sorterValue"
      :table-filter="{ external: true, lazy: false, placeholder: 'Enter here', label: 'Search:'}"
      :responsive="false"
      :loading="this.loading"
      :hover="true"
      :items-per-page-select="true"
      :items-per-page="this.perPage"
      @pagination-change="paginationChanged"
      @update:sorter-value="sorterUpdated"
      @update:table-filter-value="filterUpdated"
  >
      <slot name="requiredTableFields"></slot>
  </CDataTable>

In the parent component I have:

<TheTable
        v-bind:fields="fields"
        v-bind:endpoint-url="endpointUrl"
>
    <template slot="requiredTableFields">
        <td slot="name" slot-scope="{item}">
            <div>{{ item['attributes.email'] }}</div>
            <div class="small text-muted">
                {{ item['attributes.firstname'] }} {{ item['attributes.lastname'] }}
            </div>
        </td>
        <td slot="registered" slot-scope="{item}">
            <template v-if="item['attributes.created_at']">{{ item['attributes.created_at'] }}</template>
            <template v-else>Not setup yet</template>
        </td>
    </template>
</TheTable>

Is there any way to make it work? Cheers, Casper

1
with vue 2.6 there is a new way template v-slot:requiredTableFieldsdepperm
Sadly no luck, problem appears to be in that the table doesnt see its slots...Maarduk
CDataTable expects templates for it's slots. you need to set expected templates first. i think you are trying to pass this templates from a parent component is that correct ?.Vigikaran
Yeah, I'm trying to pass them from the parent. Actually can't set up those templates there because they will vary, component with CDataTable is going to be used for abstraction.Maarduk
I don't thing it is possible but you could try using the following library github.com/LinusBorg/portal-vueVigikaran

1 Answers

2
votes

Merging the underlying slots into a single requiredTableFields slot isn't going to work because there's no (easy) way to break the child slots back out once they've been merged.

Instead you can just keep the slots separate:

<TheTable ...>
  <template v-slot:name="{ item }">
    <td>
      ...
    </td>
  </template >
  <template v-slot:registered="{ item }">
    <td>
      ...
    </td>
  </template>
</TheTable>

This is passing two scoped slots, name and registered, into TheTable.

Assuming you don't want to hard-code the slot names into TheTable you'd then need to iterate over the $scopedSlots to include them dynamically.

<CDataTable ...>
  <template v-for="(x, slotName) in $scopedSlots" v-slot:[slotName]="context">
    <slot :name="slotName" v-bind="context" />
  </template>
</CDataTable>

Some notes on this:

  1. x is not used, we just need to loop over the slot names.
  2. The 'data' associated with the scoped slot is referred to as context and is just passed on.
  3. If the slots weren't scoped slots it'd be slightly different. We'd iterate over $slots instead and remove all the parts that refer to context.
  4. There is a : at the start of the :name attribute as we want to pass a dynamic name. It is an unfortunate coincidence that one of the slots in the original question is also called name, potentially leading to some confusion here.
  5. The v-bind="context" part is analogous to the JavaScript spread operator, if that makes it any clearer. Think of it as attributes = { name: slotName, ...context }.

Below is a complete example illustrating this technique outlined above. It doesn't use CDataTable but the core principle for passing on the slots is exactly the same.

const Comp2 = {
  template: `
    <div>
      <h4>Left</h4>
      <div><slot name="top" item="Red" /></div>
      <h4>Right</h4>
      <div><slot name="bottom" item="Green" /></div>
    </div>
  `
}

const Comp1 = {
  template: `
    <div>
      <comp2>
        <template v-for="(x, slotName) in $scopedSlots" v-slot:[slotName]="context">
          <slot :name="slotName" v-bind="context" />
        </template>
      </comp2>
    </div>
  `,
  
  components: {
    Comp2
  }
}

new Vue({
  el: '#app',

  components: {
    Comp1
  }
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>

<div id="app">
  <comp1>
    <template v-slot:top="{ item }">
      <button>Slot 1 - {{ item }}</button>
    </template>
    <template v-slot:bottom="{ item }">
      <button>Slot 2 - {{ item }}</button>
    </template>
  </comp1>
</div>