最近名前をよく見かけるsvelte/sapperを試してみた ~その2 構文編~

# はじめに
この記事は前回投稿した記事 
[最近名前をよく見かけるsvelte/sapperを試してみた ~その1 導入編~](https://qiita.com/inagacky/items/bac20282d44737e9740f)
の続きの記事になります。

今回は、 `svelte` の構文について、メインに取り上げていきたいと思います。

# 参考リンク
[svelteのサンプル集](https://svelte.dev/examples)
[svelteドキュメント](https://svelte.dev/docs)

# 前提
今回は前回作成したプロジェクトの、
`sapper_sample/src/routes/index.svelte`
(笑顔のおじさんが映っているページ)のファイルを修正していきつつ、構文の確認を行いたいと思います。
現状は下記のようになっています。
(style部分は除く)

```index.svelte
<head>
	<title>Sapper project template</title>
</head>

<h1>Great success!</h1>

<figure>
	<img alt='Borat' src='great-success.png'>
	<figcaption>HIGH FIVE!</figcaption>
</figure>

<p><strong>Try editing this file (src/routes/index.svelte) to test live reloading.</strong></p>
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/6e20b6c5-230c-84b1-701b-61936ed65952.png" width="700px">

それでは始めていきます！
(長くなるので追加部分のみ記載しています。ご了承ください。)

# 構文
## if/else
```index.svelte
<script>
	let state = { switch: false };

	function toggle() {
		state.switch = !state.switch;
	}
</script>

{#if state.switch}
	<button on:click={toggle}>
		スイッチON
	</button>
	<div>スイッチONだよ！</div>
{:else}
	<button on:click={toggle}>
		スイッチOFF
	</button>
	<div>スイッチOFFだよ！</div>
{/if}
```
上記を実行すると、下記のようにクリックでスイッチのON/OFFが切り替わるようなボタンを設置することができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/5e5e528c-53f8-f578-b336-7267ed212a6b.gif" width="600px">
## each
```index.svelte
<script>
	let members = [
		{ name: 'たろー', gender: '男性' },
		{ name: 'じろー', gender: '男性' },
		{ name: 'はなこ', gender: '女性' },
		{ name: 'さぶろー', gender: '男性' }
	];
</script>

<ul>
	{#each members as { name, gender }, i}
		<li>
			{i + 1}: {name} {gender}
		</li>
	{/each}
</ul>
```
上記を実行すると、定義したリストに応じた情報(ここの例ではメンバー)が表示されます。簡単ですね。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/aad81e29-0551-dcac-2dfb-57184966f1d1.png" width="600px">

## await
```index.svelte
<script>
	let promise = getName();

	async function getName() {
                // 遅延させる
		await new Promise(r => setTimeout(r, 5000));
		return "JIRO"
	}
</script>

{#await promise}
	<p style="font-size:20px;color:#f00;">...waiting</p>
{:then member}
	<p style="font-size:20px;">{member}</p>
{/await}

```
上記を実行すると、async関数が値(ここではJIRO)を返すまで、 `...waiting` の文字が表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/69f72db9-03e5-ea69-94fd-05ba6b830601.gif" width="600px">

# リアクティブ
```index.svelte
<script>
	let count = 0;
	function handleClick() {
		count += 1;
	}
</script>

<figure on:click={handleClick}>
	<img alt='Borat' src='great-success.png'>
	<figcaption>HIGH FIVE!</figcaption>
</figure>

<div style="text-align: center;">
	<div style="font-size:20px;">
		クリック回数：{count}
	</div>
</div>

```
上記を実行すると、 `figure` 要素(おじさん写真)をクリックするたびに、数字がインクリメントされます。
(下記画像ではおじさんを連打しています。)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/7bf1349a-6de3-9f3f-99e2-d82ecfaeadd2.gif" width="600px">

ちなみに下記のようにすると、クリックした回数×2,3が画面に表示されるようになります。

```index.svelte
<script>
	let count = 0;
	$: doubled = count * 2;
	$: tripled = count * 3;

	function handleClick() {
		count += 1;
	}
</script>

<figure on:click={handleClick}>
	<img alt='Borat' src='great-success.png'>
	<figcaption>HIGH FIVE!</figcaption>
</figure>

<div style="text-align: center;">
	<div style="font-size:20px;">
		クリック回数：{count}
	</div>
	<div style="font-size:20px;">
		クリック回数×2：{doubled}
	</div>
	<div style="font-size:20px;">
		クリック回数×3：{tripled}
	</div>
</div>
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/4d03ac31-15f7-05b0-131e-cc10b55c7fcb.gif" width="600px">

## $:の意味は？
上記の例で `$:` が出てきましたが、
`$:` ラベルが付与された式や文は、変数の更新のたびに再計算されるようになります。
この時、 `let` 等は不要になります。
代入やバインドと関係のない変数に対して、再計算を行いたい場合に用います。

# データバインディング
## textbox
```index.svelte
<script>
	let input_text = '';
</script>

<div style="text-align:center; font-size:20px;">
	<input bind:value={input_text} placeholder="入力してください。">
	<p>{input_text }!</p>
</div>
```
上記を実行すると、 テキストボックスに入力した文字が、その下に即時反映されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/95be23c7-f33b-5e12-6b51-35b9787f6708.gif" width="600px">

## checkbox
```index.svelte
<script>
	let check_status = false;
</script>

<div style="text-align: center;font-size:20px;">
	<label>
		<input type=checkbox bind:checked={check_status}>
		ここをチェック！
	</label>

	{#if check_status}
		<p>チェックしてくれてありがとう( ⁎ᵕᴗᵕ⁎ )</p>
	{:else}
		<p>チェックしてください（´・ω・｀）</p>
	{/if}
</div>

```
上記を実行すると、 チェック可否によって文字が変更されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/c0e6dfa0-0b83-1ae7-b707-c72ebbb022ed.gif" width="600px">

## selectbox
```index.svelte
<script>
	let questions = [
		{ id: 1, text: `このおじさんの名前は？` },
		{ id: 2, text: `このおじさんの年齢は？` },
	];

	let selected;

	let answer = '';
	let message = ``;

	function handleSubmit() {
		message = `質問：「${selected.text}」　答え：「${answer}」`
	}
</script>

<form on:submit|preventDefault={handleSubmit}>
	<div>
		<div style="text-align: center">
			<select bind:value={selected} on:change="{() => answer = ''}"  style="font-size:20px;">
				{#each questions as question}
					<option value={question}>
						{question.text}
					</option>
				{/each}
			</select>
		</div>
		<div style="text-align: center">
			<input bind:value={answer}  style="font-size:20px;">
			<button disabled={!answer} type=submit  style="font-size:20px;">
				Submit
			</button>

			<div style="font-size: 24px;color: #f00;">{message}</div>
		</div>
	</div>
</form>

```
上記を実行すると、 セレクトボックスの内容、テキストボックスの内容を表示します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/ed4c4b0e-c5fa-7e84-ef15-f2685460a811.gif" width="600px">


# トランジション
```index.svelte
<script>
	import { fade } from 'svelte/transition';
	let visible = true;
</script>

<div style="text-align: center;margin-bottom:40px; font-size:20px;">
<label>
	<input type="checkbox" bind:checked={visible}>
	表示する
</label>
</div>

{#if visible}
<figure transition:fade>
	<img alt='Borat' src='great-success.png'>
	<figcaption>HIGH FIVE!</figcaption>
</figure>
{/if}

```

上記を実行すると、 チェック可否によっておじさんがフェードインされます。
(GIFアニメがちょっと早かったです・・・。)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/62f567d3-a0ff-12ec-0b0f-0d25ae08df2c.gif" width="600px">

CSSと組み合わせて、下記のようなものも作れます。

```index.svelte
<script>
	import { fade } from 'svelte/transition';
	import { elasticOut } from 'svelte/easing';

	let visible = true;

	function spin(node, { duration }) {
		return {
			duration,
			css: t => {
				const eased = elasticOut(t);

				return `
					transform: scale(${eased}) rotate(${eased * 1080}deg);
					color: hsl(
						${~~(t * 360)},
						${Math.min(100, 1000 - 1000 * t)}%,
						${Math.min(50, 500 - 500 * t)}%
					);`
			}
		};
	}
</script>

<label>
	<input type="checkbox" bind:checked={visible}>
	実行
</label>

{#if visible}
	<div class="centered" in:spin="{{duration: 8000}}" out:fade>
		<span>GREAT SUCCESS!</span>
	</div>
{/if}

```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66330/13d6d942-be56-0db9-48bd-10994901eafd.gif" width="600px">

# まとめ
今回はsvelteの基本的な構文について触れました。だいぶ直感的に描けますので、わかりやすいです。
まだまだ紹介しきれていないこと、色々なことができますので、ご興味のある方は
[svelteのサンプル集](https://svelte.dev/examples)、[svelteドキュメント](https://svelte.dev/docs)を参考に試していただければと思います！

次回は、何か一つ簡単なアプリケーションを作成していこうかと考えています。
読んで頂きありがとうございました！