概要
親コンポーネントからhtml要素をカスタマイズして子コンポーネントに渡してレンダリングさせるための記法。
slotの基本
子コンポーネントでslot
タグを記述する。
親コンポーネントでは子コンポーネントを呼び出す処理を記述するが<子コンポーネント>...</子コンポーネント>
として閉じタグが必要となる。
また、親から子への値渡しはv-b ind:***
で行える。
<template>
<slot />
</template>
<template>
<子コンポーネント名>
<div>slotに渡す内容</div> // タグに限定はない
</子コンポーネント名>
</template>
<script setup lang="ts">
defineProps<{
name: string;
}>();
</script>
<template>
<section>
<div>・{{ name }}</div>
<slot />
</section>
</template>
<script setup lang="ts">
import {ref} from "vue";
import Child from "./components/Child.vue";
const name = ref("slotのサンプル");
</script>
<template>
<section>
<div>slotの利用</div>
<Child v-bind:name="name">
<p>App.vue</p>
</Child>
</section>
</template>
slotの利用
・slotのサンプル
App.vue
slotのデフォルト値と渡す値
Slotに対して親コンポーネントから必ずHTML要素が渡される保証はないため、HTML要素が渡されなかった場合を想定してデフォルトのHTML要素を設定することができる。
これをフォールバックコンテンツ(FallbackContent)という。
また、slotへ値を渡す場合は該当ファイルの変数のみで、コンポーネントをまたいだ変数の参照はできない。
<script setup lang="ts">
defineProps<{
name: string;
}>();
</script>
<template>
<section>
<div>・{{ name }}</div>
<slot>
<p>デフォルト</p>
</slot>
</section>
</template>
<script setup lang="ts">
import {ref} from "vue";
import Child from "./components/Child.vue";
const hoge = ref("hoge");
const fuga = ref("fuga");
</script>
<template>
<section>
<div>Slotの利用</div>
<Child v-bind:name="hoge">
<p>{{ hoge }} App.vue</p>
<!-- <p>{{ name }} App.vue</p> --> // エラー
</Child>
<Child v-bind:name="fuga"/>
</section>
</template>
Slotの利用
・hoge
hoge
・fuga
デフォルト
異なるhtml要素をレンダリングする
特別な内容ではないが今までのサンプルが同じhtml要素だったため備忘として作成。
<script setup lang="ts">
interface Props {
name: string;
}
defineProps<Props>();
</script>
<template>
<section>
<div>・{{ name }}さん</div>
<slot>
<p>child text</p>
</slot>
</section>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Child from "./components/Child.vue";
const listsInit: string[] = ["aaa", "bbb"];
const lists = ref(listsInit);
const hoge = ref("hoge");
const fuga = ref("fuga");
</script>
<template>
<section>
<Child v-bind:name="hoge">
<ul>
<li v-for="litst in lists" v-bind:key="litst">
{{ litst }}
</li>
</ul>
</Child>
<Child v-bind:name="fuga" />
</section>
</template>
・hogeさん
aaa
bbb
・fugaさん
child text
名前付きslot
ひとつのコンポーネントの複数の箇所に異なるHTML要素を挿入したい場合、それぞれのslotタグに名前を付けることがで、れを名前付きSlot(NamedSlots)という。
子コンポーネントでslotにname="slot名"
として親コンポーネントではv-slot:slot名
と記述する。
また、slot名がないslotは親コンポーネントでv-slot:default
と記述することで指定できる。
この際、slot名がないslotは親コンポーネントでv-slot:default
と記述がないと#document-fragmentとなる。
<slot name="slot名">...</slot>
<template v-slot:slot名>...</template>
<script setup lang="ts">
interface Props {
name: string;
}
defineProps<Props>();
</script>
<template>
<section>
<p>---</p>
<div>・default:{{ name }}</div>
<slot>
<p>default</p>
</slot>
<div>・detail</div>
<slot name="detail">
<p>child detail</p>
</slot>
</section>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Child from "./components/Child.vue";
const text = ref<string>("text");
const hoge = ref("hoge");
const fuga = ref("fuga");
const foo = ref("foo");
</script>
<template>
<section>
<Child v-bind:name="hoge" /> // 親からの値なし
<Child v-bind:name="fuga"> // v-slot:defaultなし
<template>
<p>App.vue</p>
</template>
<template v-slot:detail>
<p>{{ text }}</p>
</template>
</Child>
<Child v-bind:name="foo"> // v-slot:defaulあり
<template v-slot:default>
<p>App.vue</p>
</template>
<template v-slot:detail>
<p>{{ text }}</p>
</template>
</Child>
</section>
</template>
--- // 親からの値なし
・default:hoge
default
・detail
child detail
--- // v-slot:defaultなし
・default:fuga
・detail
text
--- // v-slot:defaultあり
・default:foo
App.vue
・detail
text
動的にコンポーネントを選択する
動的コンポーネントではコンポーネント名の代わりにcomponentタグを使って、v-bind:is
を記述する。
この場合、子コンポーネントでの特段の記述は必要がなく、temlateタグ内にhtmlを記述するだけで作成できる。
サンプルではInputとRadioのコンポーネントを作成する。
import コンポーネント名 from "./components/コンポーネント名.vue";
...
<component v-bind:is="コンポーネントを格納した変数" />
<script setup lang="ts">
import { ref } from "vue";
const inputNameModel = ref("hoge");
</script>
<template>
<input type="text" v-model="inputNameModel">
<p>{{ inputNameModel }}</p>
</template>
<script setup lang="ts">
import { ref } from "vue";
const memberType = ref(1);
</script>
<template>
<label>
<input type="radio" name="memberType" value="1" v-model="memberType">
hoge
</label>
<label>
<input type="radio" name="memberType" value="2" v-model="memberType">
fuga
</label>
<br>
<p>選択されたラジオボタン: {{ memberType }}</p>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Input from "./components/Input.vue";
import Radio from "./components/Radio.vue";
// 現在表示させるコンポーネントを表すテンプレート変数
const currentComp = ref(Input);
// コンポーネントの配列
const compList = [Input, Radio];
// コンポーネント名の配列
const compNameList: string[] = ["Input", "Radio"];
// 現在表示させているコンポーネントに対応した配列のインデックス番号
let currentCompIndex = 0;
// コンポーネントを切り替えるメソッド
const switchComp = (): void => {
// インデックス番号をインクリメント
currentCompIndex++;
// インデックス番号の判定
currentCompIndex = currentCompIndex > 1 ? 0 : currentCompIndex;
// インデックス番号に該当するコンポーネントをcurrentCompに代入
currentComp.value = compList[currentCompIndex];
}
</script>
<template>
<p>コンポーネント名: {{ compNameList[currentCompIndex] }}(index.{{ currentCompIndex }})</p>
<KeepAlive>
<component v-bind:is="currentComp" />
</KeepAlive>
<button v-on:click="switchComp">切り替え</button>
</template>
・Input
コンポーネント名: Input(index.0)
[hoge]
hoge
【切り替え】
・Radio
コンポーネント名: Radio(index.1)
◎hoge ◯fuga
選択されたラジオボタン: 1
【切り替え】
KeepAliveタグ
KeepAliveタグがないコンポーネントは、非表示になった際にunmounted状態になり、全てのデータなどが破棄される。
KeepAliveが記述されたコンポーネントは、表示されなくなった際にunmounted状態ではなく待機状態になため内部ではレンダリングされないままDOMが維持されており、入力内容なども含め現在の状態が保持される。
子から親へデータを渡す
slot props
の記法により子から親へslot経由でデータを渡すことができる。
個人的にはemitでいいとは感じるところ、、
利便性のいい場面がわかる方、教えて頂けますと幸いです。
<slot v-bind:SlotProp名="該当データのテンプレート変数名">
<template v-slot="slotProps">
<div>{{ SlotProp.該当データのテンプレート変数名 }}</div>
<script setup lang="ts">
import {reactive} from "vue";
const memberInfo = reactive({
name: "田中太郎",
state: "問題ありません。"
});
</script>
<template>
<section>
<slot v-bind:memberInfo="memberInfo">
<h1>指名:{{memberInfo.name}}</h1>
<h2>状態:{{memberInfo.state}}</h2>
</slot>
</section>
</template>
<script setup lang="ts">
<script setup lang="ts">
import Child from "./components/Child.vue";
</script>
<template>
<section>
<Child>
<template v-slot="slotProps">
<dl>
<dt>名前</dt>
<dd>{{ slotProps.memberInfo.name }}</dd>
<dt>状況</dt>
<dd>{{ slotProps.memberInfo.state }}</dd>
</dl>
</template>
</Child>
</section>
</template>
名前
田中太郎
状況
問題ありません。
v-slotの省略形
v-slotには省略形があり#
を使う。
< template #detail>