在Vue.js中传递槽(slot)到另一个槽中

3

我试图将插槽传递到插槽中,但需要传递下来的子组件无法看到它。

在子组件(TheTable)中,我有来自Core UI for Vue的表格组件(CDataTable),它需要一些插槽,我想从父级传递:

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

在父组件中,我有以下代码:
<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>

有没有办法让它工作呢? 祝好, 卡斯帕


使用Vue 2.6,有一种新的方式template v-slot:requiredTableFields - depperm
很遗憾,运气不佳,问题似乎出在表格无法看到其插槽上... - Maarduk
CDataTable 预期使用模板来填充其插槽。您需要首先设置预期的模板。我认为您正在尝试从父组件传递这些模板,是这样吗? - Vigikaran
是的,我正在尝试从父组件中传递它们。实际上,无法在那里设置这些模板,因为它们会有所不同,使用CDataTable组件进行抽象化处理。 - Maarduk
我不认为这是可能的,但你可以尝试使用以下库 https://github.com/LinusBorg/portal-vue - Vigikaran
1个回答

7
将底层插槽合并为一个名为requiredTableFields的插槽是行不通的,因为一旦合并了子插槽,就没有(简单的)方法将它们分解回来。
相反,您可以将这些插槽分开保持独立。
<TheTable ...>
  <template v-slot:name="{ item }">
    <td>
      ...
    </td>
  </template >
  <template v-slot:registered="{ item }">
    <td>
      ...
    </td>
  </template>
</TheTable>

这里传递了两个作用域插槽,nameregistered,到TheTable组件中。

假设您不想将插槽名称硬编码到TheTable组件中,则需要动态迭代$scopedSlots来包含它们。

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

关于这个问题的一些说明:

  1. x没有用到,我们只需要循环遍历插槽名称即可。
  2. 与作用域插槽相关联的“data”被称为context,并直接传递。
  3. 如果插槽不是作用域插槽,则略有不同。我们会遍历$slots并删除所有引用context的部分。
  4. :name属性开头有一个:符号,因为我们想要传递一个动态的名称。原问题中的一个插槽也叫name,这可能会导致一些混淆。
  5. v-bind="context"部分类似于JavaScript spread运算符,如果这能让您更加清楚明白的话。可以将其视为attributes = { name: slotName, ...context }

下面是一个完整的示例,演示了上述技巧。它不使用CDataTable,但传递插槽的核心原理完全相同。

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/vue@2.6.11/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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接