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

Как создать компонент для таблицы с двумя колонками в 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`, он будет корректно отображать заголовки и тела в таблице, используя заданные слоты для каждого столбца. Каждый столбец будет захватывать свои слоты заголовков и тел, передавая нужные данные в соответствующие шаблоны.
<a href="https://play.vuejs.org/#eNq1VcFO20AQ/ZWROSRIwYEEVNUNoBZxaA8tKtwwB8feJAubXcu7DlRRpJYfQOqpP1EJtZWoSsU3OH/U2TXe2IEm9NBDFO/Om5k3zzPjsfMyjt1RShzP6cgwobECSVQa7/icDmORKBhDQnowgV4ihlBDaO2FtR0FXUb2UqnQlNvdZulOB36AFmwOKliB83kouFQQBSqAbZ23fuxzQAo08mCjATwYEg9qQRDUGhD08XmzDZPGDNOymG63W2BaWxVM22LCMCww8LyC2bSYKIoKzLOWwZysItVOM1cLdcKDIsOYBYrgCaBTVsXTtWz7jv7zHWO3CFQiTkSMVhr5DrCgSxgeVhDXsdqYBI86IaeSV/Y5u8tuph+z6+mn6SUazmmkBmjY2Fq3iTFKQRVWBiSISGItaIvoaCf7Or3M7iC7ya6z7xjyFn/f8PcLsh/Z7fQKzMkmyq47Te1m4zcrWsyl7IroA1KSoYiR+4PUlRI8GI/BIN1EnLtYLUwmS5I9RTb9Xsu6fcl+T69KerXW/6LXEvI/dZwqZ53qH0nPjqZ/8KaEdxqOwui8R/vuqRQch3asXXwnFMOYMpK8ixXFEfIdVC+Pjn3CmDh/Y+5UkhLT5cZnQMKzR+5P5YW+852DhEiSjLBYa1NB0icqN+8fviUX+GyNQxGlDNELjO+JFCzVHHPYq5RHSLuEM2xfm4VBef9I7l8owmVRlCaqkfms+g4ujr0Fpc/ott228fP5BFWc21GLll8qySETSs5vwGJX6a6SuKwi0qOcHOhT/RjXBs57DcyqyHEh1j3kGllErK+66BSkTO269WU7RWnGRcMoPbqlFq0MsRrAaK0nEmzVcc7Og7gB4YCyKCHcgxBLobwghJ3vnRHd2LGr0ZXOxnC6swQnXGFU2kNYuOvmm0N7UqlvZhfNqrMdntEaYZLs4HTErpk8MxYPVoWZjkF5VIrS9P2s6I7Sw1hWwNaMc6erq+zbHBP9d100qZIqxREp5cSeJBDijvOUJwtEKr3+ikhWF3y+b5lKCNv8+Vf3kc6fa2RNBD+ANfPW7ju61KjO5A+lrL9Q" rel="nofollow">Вот так</a> : <br/> <br/> <pre><code class="javascript">import { useSlots } from 'vue';

const columns = useSlots().default?.();</code></pre> <br/> <pre><code class="html">&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th v-for="{ props: p, children: c } in columns" :key="p.prop"&gt;
        &lt;component v-if="c?.header" :is="c.header" /&gt;
        &lt;template v-else&gt;{{ p.label }}&lt;/template&gt;
      &lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr v-for="row in data"&gt;
      &lt;td v-for="{ props: p, children: c } in columns" :key="p.prop"&gt;
        &lt;component v-if="c?.body" :is="c.body" :row="row" /&gt;
        &lt;template v-else&gt;{{ row[p.prop] }}&lt;/template&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;</code></pre>
Похожие вопросы