Как отобразить дочерние слоты в родительском компоненте?

Как создать компонент для таблицы с двумя колонками в UI? 

Требуется реализовать таблицу, в которую передается массив данных и две колонки. Колонки должны использовать слоты (для заголовка и тела), чтобы правильно отображать информацию в заголовке и теле таблицы.

Вот пример кода для реализации таблицы:

<pre><code class="javascript">&lt;TableCustom :data="data"&gt;
    &lt;TableCol prop="age" label="Возраст" width="150"&gt;
      &lt;template #header&gt;
        &lt;div&gt;Это заголовок для возраста&lt;/div&gt;
      &lt;/template&gt;
      &lt;template #body="scope"&gt;
        &lt;div&gt;Возраст: {{ scope.row.name }}&lt;/div&gt;
      &lt;/template&gt;
    &lt;/TableCol&gt;

    &lt;TableCol prop="name" label="Имя" width="200"&gt;
      &lt;template #header&gt;
      &lt;/template&gt;
      &lt;template #body="scope"&gt;
        &lt;div&gt;имя {{ scope.row.name }}&lt;/div&gt;
      &lt;/template&gt;
    &lt;/TableCol&gt;
  &lt;/TableCustom&gt;</code></pre>

Вопрос состоит в следующем: допустим, существует реализация компонента. Как правильно достать слоты из объекта children и отобразить их в нужных местах шаблона, чтобы данные корректно рендерились?
  • 18 декабря 2024 г. 17:13
Ответы на вопрос 2
Чтобы правильно отобразить дочерние слоты в родительском компоненте, который, в вашем случае, будет таблицей с двумя колонками, вы можете воспользоваться функциональностью Vue.js для работы с слотами. Давайте рассмотрим, как можно реализовать компоненты `TableCustom` и `TableCol`, чтобы корректно отображать заголовки и тела в вашей таблице.

Вот пример реализации компонента `TableCustom` и `TableCol`, который использует слоты:

```vue
<!-- TableCustom.vue -->
<template>
  <table>
    <thead>
      <tr>
        <th v-for="(col, index) in columns" :key="index" :style="{ width: col.width }">
          <slot :name="`header-${col.prop}`" />
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(row, rowIndex) in data" :key="rowIndex">
        <td v-for="(col, colIndex) in columns" :key="colIndex">
          <slot :name="`body-${col.prop}`" :scope="{ row }" />
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    data: {
      type: Array,
      required: true
    }
  },
  computed: {
    columns() {
      return this.$slots.default().map(slot => {
        return { prop: slot.props.prop, width: slot.props.width };
      });
    }
  }
}
</script>
```

```vue
<!-- TableCol.vue -->
<template>
  <div style="display: none;">
    <slot></slot>
  </div>
</template>

<script>
export default {
  props: {
    prop: {
      type: String,
      required: true
    },
    label: {
      type: String,
      required: true
    },
    width: {
      type: String,
      required: false
    }
  }
}
</script>
```

Здесь `TableCustom` является родительским компонентом, который получает массив данных через пропс `data`. Он использует слоты, чтобы отобразить пользовательски определенные заголовки и тела ячеек. Мы определяем `header` и `body` слоты с помощью шаблонов с именами на основе `prop` из `TableCol`.

Теперь вы можете использовать компоненты `TableCustom` и `TableCol` следующим образом:

```vue
<template>
  <TableCustom :data="data">
    <TableCol prop="age" label="Возраст" width="150">
      <template #header>
        <div>Это заголовок для возраста</div>
      </template>
      <template #body="{ row }">
        <div>Возраст: {{ row.age }}</div>
      </template>
    </TableCol>

    <TableCol prop="name" label="Имя" width="200">
      <template #header>
        <div>Это заголовок для имени</div>
      </template>
      <template #body="{ row }">
        <div>Имя: {{ row.name }}</div>
      </template>
    </TableCol>
  </TableCustom>
</template>

<script>
export default {
  data() {
    return {
      data: [
        { age: 25, name: 'John' },
        { age: 30, name: 'Jane' },
        // больше данных...
      ]
    };
  }
}
</script>
```

Теперь, когда вы используете компонент `TableCustom`, он будет корректно отображать заголовки и тела в таблице, используя заданные слоты для каждого столбца. Каждый столбец будет захватывать свои слоты заголовков и тел, передавая нужные данные в соответствующие шаблоны.
Вот так : 

import { useSlots } from 'vue';

const columns = useSlots().default?.();

<table>
  <thead>
    <tr>
      <th v-for="{ props: p, children: c } in columns" :key="p.prop">
        <component v-if="c?.header" :is="c.header" />
        <template v-else>{{ p.label }}</template>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="row in data">
      <td v-for="{ props: p, children: c } in columns" :key="p.prop">
        <component v-if="c?.body" :is="c.body" :row="row" />
        <template v-else>{{ row[p.prop] }}</template>
      </td>
    </tr>
  </tbody>
</table>
Похожие вопросы