Appearance
Slot
親コンポーネントから子コンポーネントにPropsでデータを渡すことが出来ますが、 Propsではhtml要素を直接渡すことが出来ません。
<slot/>
を使用することで、html要素を子コンポーネントに渡すことが可能になります。
UIコンポーネントのライブラリー(Vuetify
など)を使用する場合にも使う方法です。
サンプル
レイアウト(外側)とコンテンツ(内側)の例です。
レイアウトのコンポーネントで、コンテンツ側に表示する要素を記述しています。
子コンポーネントで表示レイアウトをハンドリングしつつ、親コンポーネントから動的に表示する内容を変えたりすることが出来ます。
イメージは、ブログ記事の構成になります。
レイアウト
コンテンツ
コンテンツを表示する
コンテンツの最後
レイアウトの最後
vue
<template>
<div class="layout">
<h3>
レイアウト
</h3>
<TemplateChild>
<template #default>
<h4>
コンテンツ
</h4>
<p>コンテンツを表示する</p>
<p>コンテンツの最後</p>
</template>
</TemplateChild>
<p>レイアウトの最後</p>
</div>
</template>
vue
<template>
<div class="contents">
<slot/>
</div>
</template>
Slotのデフォルト
親コンポーネントから必ずhtml要素が渡されない場合、<slot></slot>
で挟んだhtml要素を表示することが出来ます。
ブログ記事のページが無い時、404エラーを表示する場合にも使えます。
(勿論、v-if
など他の方法でもOKです。)
サンプル
レイアウト
404 Not found!
レイアウトの最後
vue
<template>
<div class="layout">
<h3>
レイアウト
</h3>
<TemplateChildFallback>
<template #default>
</template>
</TemplateChildFallback>
<p>レイアウトの最後</p>
</div>
</template>
vue
<template>
<div class="contents">
<slot>
404 Not found!
</slot>
</div>
</template>
名前付きSlot
レイアウト(外側)からコンテンツ(内側)へhtml要素を渡すのは、1種類とは限りません。
複数のhtml要素を別々に渡したい場合があります。
例えば、コンテンツの他にサイドカラムに表示する内容やコンテンツ下に表示する内容を、別々のコンポーネントに分けている場合です。
サンプル
親コンポーネント側のhtml要素を<template v-slot:contents>
で名前付きslotに渡します。<template #contents>
のようにv-slot:は#で省略できます。
子コンポーネント側で<slot name="contents">
のように名前付slotを記述します。
名前無しのslotは、<slot name="default">
と対応します。
レイアウト
コンテンツ
コンテンツを表示する
コンテンツの最後
関連コンテンツを表示する
レイアウトの最後
vue
<template>
<div class="layout">
<h3>
レイアウト
</h3>
<TemplateChildNamed>
<template #contents>
<h4>
コンテンツ
</h4>
<p>コンテンツを表示する</p>
<p>コンテンツの最後</p>
</template>
<template #after-contens>
<p>関連コンテンツを表示する</p>
</template>
</TemplateChildNamed>
<p>レイアウトの最後</p>
</div>
</template>
vue
<template>
<div class="contents">
<slot name="contents"/>
</div>
<div class="after-contents">
<slot name="after-contens"/>
</div>
</template>
スコープ付きSlot
今までは親コンポーネントから子コンポーネントにhtml要素を渡す為slotを使用しました。
ここでは子コンポーネントのデータを親コンポーネントに渡す方法の紹介になります。
サンプル
少し強引なサンプルですが、コンテンツ側に配置したいいねボタンを押すことで、コンテンツ上部のいいねの数がカウントアップします。
サンプルのようにコンテンツ側のリアクティブな値を、レイアウト側のtemplate内で使うことが出来ました。
レイアウト
コンテンツ
いいね : 0
コンテンツを表示する
コンテンツの最後
レイアウトの最後
vue
<template>
<div class="layout">
<h3>
レイアウト
</h3>
<TemplateChildScoped>
<template #contents="{contentsInfo}">
<h4>
コンテンツ
</h4>
<p>いいね : {{ contentsInfo }}</p>
<p>コンテンツを表示する</p>
<p>コンテンツの最後</p>
</template>
</TemplateChildScoped>
<p>レイアウトの最後</p>
</div>
</template>
vue
<template>
<div class="contents">
<slot
name="contents"
:contentsInfo="contentsInfo"
/>
<button
class="btn"
@click="contentsInfo++"
>いいね</button>
</div>
</template>
vue
<script setup lang="ts">
import { ref } from 'vue';
const contentsInfo = ref(0)
</script>