nuxt.svg

【Nuxt 2】スロット(slot)

Nuxt2

スロットコンテンツ

以下のようにmy-buttonコンポーネントを作成します。

MyButton.vue
<template>
  <button>
    <slot></slot>
  </button>
</template>

<slot>には、以下のようにmy-button内に記述したコンテンツが展開されます。

<my-button>Click Me!</my-button>
展開結果
<button>Click Me!</button>

当然テキストだけでなく、要素や他のコンポーネントも展開されます。

<my-button><span class="bold">Click Me!</span></my-button>
展開結果
<button><span class="bold">Click Me!</span></button>

<slot>内のコンテンツは、デフォルト値となります。

MyButton.vue
<template>
  <button>
    <slot>My Button!</slot>
  </button>
</template>
<my-button></my-button> <my-button>Click Me!</my-button>
展開結果
<button>My Button!</button>
<button>Click Me!</button>

名前付きスロット

以下のように<slot>name属性を設定することで、複数のスロットを使用することができます。

MyCard.vue
<div class="card">
  <div class="card__header">
    <slot name="header"></slot>
  </div>
  <div class="card__contents">
    <slot></slot>
  </div>
  <div class="card__footer">
    <slot name="footer"></slot>
  </div>
</div>

コンポーネントを使用する際は、<template>v-slotを使って名前を指定します。

<my-card>
  <template v-slot:header>
    <p>My Card Title</p>
  </template>
  <template v-slot:default>
    <ul>
      <li>contents1</li>
      <li>contents2</li>
      <li>contents3</li>
    </ul>
  </template>
  <template v-slot:footer>
    <p>My Card Footer</p>
  </template>
</my-card>
展開結果
<div class="card">
  <div class="card__header">
    <p>My Card Title</p>
  </div>
  <div class="card__contents">
    <ul>
      <li>contents1</li>
      <li>contents2</li>
      <li>contents3</li>
    </ul>
  </div>
  <div class="card__footer">
    <p>My Card Footer</p>
  </div>
</div>

<template v-slot:default>は省略することができます。

またv-slot#で省略することができます。

<my-card>
  <template #header>
    <p>My Card Title</p>
  </template>
  <template #default>
    <ul>
      <li>contents1</li>
      <li>contents2</li>
      <li>contents3</li>
    </ul>
  </template>
  <template #footer>
    <p>My Card Footer</p>
  </template>
</my-card>

スロットプロパティ

スロットプロパティとは、親が<slot>を通じてコンポーネントのデータやプロパティにアクセスするためのものです。 以下の例をみてください。

UserView.vue
<template>
  <div>
    <p>
      <slot #id :user="user">{{ user.id }}</slot>
      <slot #name :user="user">{{ user.name }}</slot>
    </p>
  </div>
</template>

<script>
export default {
  data: () => ({
    user: {
      id: 1,
      name: 'Taro'
    }
  })
}
</script>
表示結果
1
Taro

注目する点は、<slot>でバインドしている:user="user"の部分です。 このようにバインドした属性は、スロットプロパティとして設定されます。

スロットプロパティは、v-slot(あるいは#)で以下のように親から参照することができます。 参照した値はスロット内でのみ使用可能です。

<user-view>
  <template #name="{ user }">{{ user.name.toUpperCase() }}</template>
</user-view>
表示結果
1
TARO

サンプル(table)

ID名前年齢性別
1TANAKA20歳
2SUZUKI26歳
3YAMADA30歳
4ITO18歳不明
5KATO40歳
MyTable.vue
<template>
  <table>
    <thead>
      <tr>
        <th v-for="header in headers" :key="header.value">
          {{ header.text }}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(item, i) in items" :key="i">
        <td v-for="header in headers" :key="header.value">
          <slot :name="header.value" :item="item">
            {{ item[header.value] }}
          </slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    headers: {
      type: Array,
      required: true,
    },
    items: {
      type: Array,
      required: true,
    },
  },
}
</script>
Page
<template>
  <my-table :headers="headers" :item="items">
    <template #id="{ item }">
      <span style="color: red">{{ item.id }}</span>
    </template>
    <template #name="{ item }">
      {{ item.name.toUpperCase() }}
    </template>
    <template #age="{ item }">
      {{ item.age }}歳
    </template>
  </my-table>
</template>

<script>
export default {
  data: () => ({
    headers: [
      { text: 'ID', value: 'id' },
      { text: '名前', value: 'name' },
      { text: '年齢', value: 'age' },
      { text: '性別', value: 'gender' },
    ],
    items: [
      { id: 1, name: 'Tanaka', age: 20, gender: '男' },
      { id: 2, name: 'Suzuki', age: 26, gender: '女' },
      { id: 3, name: 'Yamada', age: 30, gender: '女' },
      { id: 4, name: 'Ito', age: 18, gender: '不明' },
      { id: 5, name: 'Kato', age: 40, gender: '男' },
    ],
  }),
}
</script>