スロット

スロット は、親コンポーネントから子コンポーネントの特定の場所にテンプレートを差し込むための仕組みです。 コンポーネントを「箱」として使い、その中に好きなHTMLや部品を入れることができます。

<!-- AppButton: 子コンポーネント -->
<template>
  <button type="button" class="app-button">
    <slot /> <!-- スロットアウトレット -->
  </button>
</template>

<style scoped>
.app-button {
  padding: 0.375rem 1rem;
  border-radius: 0.375rem;
  border: none;
  font-size: 0.875rem;
  background-color: #02c169;
  color: #fff;
}
</style>
<!-- 親コンポーネント -->
<template>
  <AppButton>
    <!-- ↓ スロットコンテンツ -->
    <span></span>
    <span class="padding-start-1">すべて完了にする</span>
    <!-- ↑ スロットコンテンツ -->
  </AppButton>
</template>

<slot> 要素は、親が提供した スロットコンテンツ をレンダリングすべき場所を示す スロットアウトレット です。

parent template <AppButton> ✅ すべて完了にする </AppButton> slot content replace <AppButton> template <button> <slot/> </button> slot outlet

フォールバックコンテンツ(デフォルト)

親からスロットコンテンツが渡されなかった場合に、フォールバック(つまりデフォルト)を設定することもできます。 やり方は <slot> 要素の間に、フォールバックコンテンツを設定します。

<!-- AppButton: 子コンポーネント -->
<template>
  <button type="button" class="app-button">
    <slot>
      ボタン <!-- フォールバックコンテンツ -->
    </slot>
  </button>
</template>

<style scoped>
/* 省略 */
</style>
<!-- 親コンポーネント -->
<template>
  <AppButton />
</template>

この例では、親コンポーネントから、 slot を渡していませんが、 フォールバックコンテンツを設定しているので、「ボタン」と表示されます。


名前付きスロット

<slot> に名前を付けると、1つのコンポーネントに複数の差し込み場所(スロットアウトレット)が作れます。

名前付きのスロットコンテンツを渡すためには、 v-slot を利用します。(例: <template v-slot:title>

v-slot# で省略表記ができます。(例: <template #title>

<!-- AppModal: 子コンポーネント -->
<template>
  <div
    role="dialog"
    aria-labelledby="dialogTitle"
    aria-describedby="dialogDesc"
    class="modal"
  >
    <h2 id="dialogTitle">
      <slot name="title" /> <!-- titleという名前の スロットアウトレット -->
    </h2>
    <div id="dialogDesc">
      <slot />
    </div>
  </div>
</template>
<!-- 親コンポーネント -->
<template>
  <AppModal>
    <template #title>
      <!-- v-slot. 上のように # で省略表記ができます -->
      <!-- ↓ titleという名前の スロットコンテンツ -->
      <span>✏️</span>
      <span class="padding-start-1">タスクの編集</span>
      <!-- ↑ titleという名前の スロットコンテンツ -->
    </template>

    <form>
      <div>
        <label for="title">タイトル</label>
        <input id="title" v-model="inputTitle" type="text" required>
      </div>
    </form>
  </AppModal>
</template>

名前なし(name を持たない)スロットは、暗黙的に default という name を持つものとされますので、 以下のようにも記述できます。

<!-- 親コンポーネント -->
<template>
  <AppModal>
    <template #title>
      <span>✏️</span>
      <span class="padding-start-1">タスクの編集</span>
    </template>

    <template #default>
      <form>
        <div>
          <label for="title">タイトル</label>
          <input id="title" v-model="inputTitle" type="text" required>
        </div>
      </form>
    </template>
  </AppModal>
</template>

ポイント

  • <slot name="xxx" /> に対して <template #xxx> で中身を渡す
  • 名前なしスロット(デフォルト)と混ぜて使える

まとめ

  • スロットは、コンポーネントの中の特定位置にテンプレートを差し込む機能
  • slot は、フォールバック(デフォルト)を設定することもできる
  • 名前付きスロットを使えば、差し込み場所を増やせる

現在の実装の課題

  • タスクを新規作成するためのフォームが表示されていません。
  • AppModal コンポーネントのタイトルが「新規作成モーダル」という、固定の名前になっていて、汎用性がない状態になっています。

チャレンジ

1. AppModal.vue<slot> を追加

AppModal.vue に、

  • モーダルのタイトル
  • モーダルのコンテンツ

が差し込みできるよう、 <slot> を追加してみましょう。

  1. AppModal.vue<h2>新規作成モーダル</h2> を削除して <slot name="title" /> を追加しましょう。
  2. AppModal.vue に、モーダルコンテンツを追加するための <slot /> を追加しましょう。

2. AppModal コンポーネントに、モーダルタイトルと新規作成フォームの v-slot を追加

  1. app.vueAppModal コンポーネントに、モーダルタイトルの v-slot <template #title><h2>タスクの新規作成</h2></template> を追加しましょう。
  2. app.vueAppModal コンポーネントに、 v-slot (名前なし)に、以下のHTMLを追加しましょう。
<form>
  <div>
    <label for="title">タイトル</label>
    <input id="title" type="text" required />
  </div>

  <div>
    <label for="note">メモ</label>
    <textarea id="note" rows="2" />
  </div>

  <div>
    <label for="dueDate">期限</label>
    <input id="dueDate" type="date" />
  </div>

  <div>
    <button type="submit">登録</button>
  </div>
</form>

もし行き詰まったら、以下のボタンをクリックして解答を見ることができます。

コンポーネントの v-model
フォーム入力バインディングで紹介したように、 v-model はフォーム入力欄とVueのデータを自動で同期してくれる仕組みです。 この v-model は、コンポーネントでも同じように使うことができます。
復習・まとめ
TODOリストの作成を通して学べる、基本的なVue.jsの機能の説明は以上になります。
ファイル
エディタ
WebContainerを初期化中
ファイルをマウント中
依存関係をインストール中
Nuxtサーバーを起動中
Nuxtが準備完了を待機中
ターミナル