SvelteのSlot(スロット)を図解で理解する。

## SvelteのSlotについて、よく分からなかったので、図を交えて理解してみました。

親コンポーネントから、子コンポーネント呼び出しタグの中に要素を差し込むと、子コンポーネントのslotタグの位置に要素を表示させることができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/614609b9-4263-2181-d64a-ff1235bcf2e0.png" width="500px">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/fb38550d-70ed-e49c-28d1-04d23335fd24.png" width="900px">


## デフォルトスロット

slotタグ内にデフォルトの要素を記載した場合、親コンポーネントからスロットに要素を渡さなかった場合、デフォルトの要素が表示されます。

```vue
//App.svelte

<script>
  import Box from './Box.svelte';
</script>

<Box></Box>
```

```vue
//Box.svelte

<div>
  <slot>
	<p>デフォルトメッセージ</p>
  </slot>
</div>
```

## 名前付きスロット

スロットに名前をつけて、親要素から差し込んだ要素の位置を制御することができます。

```親コンポーネント.
 slot="差し込みたいスロットの名前"
```

```子コンポーネント .
<slot name="このスロットに付けたい名前">
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/38b3da26-d6de-a035-70df-d1542f63375d.png" width="600px">
<br>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/c324e1d6-17c3-fe69-dd95-916ae391bf04.png" width="300px">


slotの名前で制御しているため、順番は不同でも問題ありません。また、スロットの名前を指定していない要素は、全て名前の無いslotに集約されます。
よって下記のように親コンポーネントでのスロットへ差し込む要素の順は不同でも問題ありません。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/42efa3fe-1dcb-0c72-3e17-65c30e916c1a.png" width="500px">


## 親コンポーネントから特定のslotに対して要素の差し込みの有無で子コンポーネントを制御する。

子コンポーネント側で **$$slots.＜slot名＞** で特定のslotに対し、親コンポーネントから差し込みがあったか否かで制御を行うことができます。。

### 実装イメージ
・親コンポーネントから子コンポーネントのclスロットに対し、差し込みがあれば背景色を赤にする。
・親コンポーネントから子コンポーネントのclスロットに対し、差し込みがあれば「コメントがありました。」と底部に表示させる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/2b0beac5-56be-6c02-72d6-b4fe31e518e0.png" width="500px">

```vue
//App.svelte

<script>
	import Diary from './Diary.svelte'
</script>

  <div style="display:flex">
	<Diary title="今日の天気は？">
	  <div slot="cl">
		<p>今日は晴れでした。</p>
	  </div>		
	</Diary>
    <!-- このDiaryに対しclスロットに要素を差し込んでいるので、子コンポーネントで $$slots.cl は true となる。 -->

	<Diary title="明日の天気は？"/>
    <!--このDiaryに対しclスロットに要素を差し込んでいないので、子コンポーネントで $$slots.cl は false となる。 -->
</div>

```

```vue
//Diary.svelte

<script>
	export let title;
</script>

<div class={ $$slots.cl ? "has-comment" : ""}
     style="border:solid 1px black; width: 300px;">
 <h2>{title}</h2>

 <slot name="cl"></slot>
  {#if $$slots.cl}
	コメントがありました。
  {/if}
</div>

<style>
	.has-comment{
		background-color: red;
	}
</style>
```

## 親コンポーネントからの差し込み要素を子コンポーネント側から操作する。

下記のような実装があるとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/57e162a9-31e2-06b4-d226-dbd5133a5fc1.png)
```vue
//App.svelte

<script>
	import Hoverable from './Hoverable.svelte';
</script>

{#each Array(3) as _}
  <Hoverable>
    <div>
      <p>マウスが乗っていません。</p>
    </div>
  </Hoverable>
{/each}

<style>
  .active {
	background-color: #ff3e00;
	color: white;
  }
</style>
```

```vue
//Hoverable.svelte

<script>
  let hovering;

  function enter() {
	hovering = true;
  }

  function leave() {
	hovering = false;
  }
</script>

<div on:mouseenter={enter} on:mouseleave={leave}>
	<slot></slot>
</div>
```
下記のイメージのように、子コンポーネントにマウスが乗った場合に背景色と文字が変わる実装を追加します。
![0e98f4f7e9712f1a4e9fa59496ee7bcd (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/3ac388bd-2ec0-a186-127a-58fb191cadd1.gif)
現時点では、子コンポネーントのslotにマウスが乗ると各子コンポーネント内のhoveringがtrueに変わりますが、親コンポーネントに伝わりません。
hoveringの値を、親コンポーネントとバインドして、マウスが乗っている場合にスロットに差し込んだ要素にactiveクラスをつけます。

```子コンポーネント.
<slot 親コンポーネントと共通の変数名={子コンポーネント側で使用する変数名}></slot>
```

```親コンポーネント.
<子コンポーネント名 let:親コンポーネントと共通の変数名={親コンポーネント側で使用する変数名}></slot>
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/46d22d8d-af03-ddc6-6e12-66e62dbc05ba.png" width="500px">

親子で共通のisActiveから、それぞれで定義した変数名(親側active、子側hovering)で値を取り出すことが可能です。