Прохождение слота в слот в Vue. js - PullRequest
1 голос
/ 21 февраля 2020

Я пытаюсь передать слот в слот, но дочерний компонент, который требует переданного слота, не видит его.

В дочернем (TheTable) у меня есть компонент таблицы из Core UI для 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>

Есть ли способ заставить его работать? Ура, Каспер

1 Ответ

1 голос
/ 21 февраля 2020

Объединение базовых слотов в один requiredTableFields не будет работать, потому что нет (простого) способа вырвать дочерние слоты обратно после их объединения.

Вместо этого вы можете просто держите отдельные слоты:

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

Это позволяет передать два слота с областями действия, name и registered, в 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 не используется, нам просто нужно l oop над именами слотов.
  2. «Данные», связанные с определенным слотом, называются context и просто передаются.
  3. Если бы слоты не были ограничены слотами, это было бы немного иначе. Вместо этого мы перебрали бы $slots и удалили все части, которые ссылаются на context.
  4. В начале атрибута :name есть :, так как мы хотим передать динамику c имя. К сожалению, одно из мест в исходном вопросе также называется name, что может привести к некоторой путанице.
  5. Часть v-bind="context" аналогична оператору распространения JavaScript, если это делает это более ясным. Думайте об этом как 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>
...