Svelte 입문 강의 - A부터 Z까지 3

3️⃣

🖥 데이터 바인딩 기초

6 - 1. input 태그

<script>
	// type="text"
	let name = 'bill'; 
	// type="number", "range"
	let a = 1;
	// type="checkbox" & if
	let yes = false;
	// type = "radio" 
	let picked = null;
	// type="checkbox" & each
	const names = ['bill', 'test', 'world']
	let checkedNames = [];
</script>

<input type="text" bind:value={name}>
<h1>{name}</h1>

<input type="number" bind:value={a} min="0" max="10">
<input type="range" bind:value={a} min="0" max="10">

<h1>{a}</h1>

<input type="checkbox" bind:checked={yes}>
{#if yes}
	<p>YES</p>
{:else}
	<p>NO</p>
{/if}


<label>
	<input type="radio" value="One" bind:group={picked}>
	One
</label>
<label>
	<input type="radio" value="Two" bind:group={picked}>
	Two
</label>

<h1>{picked}</h1>


{#each names as name, index (index)}
<label>
	<input type="checkbox" value={name} bind:group={checkedNames}>
	{name}
</label>
{/each}

<h1>{checkedNames}</h1>

6 - 2. textarea 태그

<script>
	let value = 'skarsgard'
</script>

<textarea bind:value={value}></textarea>
// 아래와 같이 생략할 수 있다.
<textarea bind:value></textarea>
<p>{value}</p>

6 - 3. select 태그

<script>
	let list =[
		{id:1, text:'james'},
		{id:2, text:'bill'},
		{id:3, text:'tom'},
	]
	let selected= [];
</script>


<select bind:value={selected} multiple>
	{#each list as item }
	<option value={item}>{item.text}</option>
	{/each}
</select>

<p>선택함 : {selected ? selected.id : '기다리는중...'}</p>
<p>선택함 : {selected ? selected.map(x => x.id).join(',') : '기다리는중...'}</p>

6 - 4. contenteditable 기능

<script>
	let html = '<p>billl</p>'
</script>

<div 
	contenteditable="true"
	bind:innerHTML={html}
></div>

<pre>{html}</pre>

<style>
	[contenteditable] {
		padding: 10px;
		border: 1px solid blue;
	}
</style>

6 - 5. Each 블록 바인딩

<script>
	let todos = [
		{ done:false, text:''}
	]
	$ : remaining = todos.filter(x => !x.done).length

	function add(){
		todos = todos.concat({done:false, text: ''})
	}

	function clear(){
		todos = todos.filter(x => !x.done)
	}
	
</script>

{#each todos as todo, index (index)}
	<div class:done={todo.done}>
		<input type="checkbox" bind:checked={todo.done}>
		<input type="text" bind:value={todo.text}>
	</div>
{/each}
<p>남은 할일 : {remaining}</p>

<button on:click={add}>추가</button>
<button on:click={clear}>완료한 애들 삭제~</button>

<style>
	.done {
		opacity: 0.4;
	}
</style>

🖥 데이터 바인딩 고급

7 - 1. Media 태그

강의내용이 부족해서.. 문서확인하는게 좋을 것 같다.

아래 목록은 read only로 바인딩 할 수 있는 8가지 속성입니다.

binding은 가능하지만 수정은 할 수 없는 8가지 속성


아래 목록은 write와 read 둘 다 가능한 4가지 속성입니다.

7 - 2. Dimension 바인딩

<script>
	let w;
	let h;
	let size = 42;
	let text = 'skarsGard'
</script>

<input type="range" bind:value={size}>

<p>너비 : {w}, 높이 : {h}</p>

<div bind:clientWidth={w} bind:clientHeight={h}> 
	<span style="font-size: {size}px;">{text}</span>
</div>

<style>
	div{
		display: inline-block;
	}
</style>

7 - 3. this 바인딩

// 부모 
<script>
	import Child from './Child.svelte'
	let first;
	let second;

	function handleClick(){
		console.log(first);
		console.log(second);
		
		
	}
</script>

<button on:click={handleClick}>ddd</button>
<div bind:this={first}>DIV</div>
<Child bind:this={second}></Child>

// 자식 
<span>Child</span>

7 - 4. 컴포넌트 Props 바인딩

// 부모 컴포넌트
<script>
	import Child from './Child.svelte'
	let number = 3;
</script>

<Child bind:number={number}></Child>
<p>{number}</p>

// 자식 컴포넌트
<script>
    export let number;

    function handleClick(){
        number *= number 
    }
</script>

<button on:click={handleClick}>제곱하기</button>

🖥 라이프 사이클

8 - 1. 라이프 사이클이란

8 - 2. onMount

// 부모컴포넌트
<script>
	import Child from './Child.svelte'
	let condition =true;
</script>

<button on:click={()=> condition = !condition}>toggle</button>

{#if condition}
<Child></Child>
{/if}

// 자식컴포넌트
<script>
    import { onMount } from 'svelte';
    let photos = [];

    // 아래는 데이터를 불러올때
    onMount(async()=> {
        const res = await fetch('https://jsonplaceholder.typicode.com/photos?_limit=20');
        photos = await res.json();
        console.log(photos);
    })

    // 아래는 데이터를 불러오고, 사라졌을때의 처리까지 해줄때
    onMount(()=> {
        fetch('https://jsonplaceholder.typicode.com/photos?_limit=20')
        .then (async(res)=>{
            photos = await res.json();
        })
        
        return ()=>{
            console.log('사라졌다')
        }
    })
</script>

{#each photos as photo (photo.id)}
<figure>
    <img src={photo.thumbnailUrl} alt={photo.title}>
    <figcaption>{photo.title}</figcaption>
</figure>
{:else} 
<p>is loading....</p>
// 반복문에서도 else 를 사용할 수 있다. 상위 컴포넌트에서 if 를 썻기때문인듯!
{/each}

8 - 3. onDestroy

// 부모 컴포넌트
<script>
	import Child from './Child.svelte'
	let condition = true;
</script>

<button on:click={()=> condition = !condition}></button>

{#if condition}
<Child></Child>
{/if}

// 자식 컴포넌트
<script>
    import { onMount } from 'svelte'
    import { onDestroy } from 'svelte'
    let seconds = 0;

    const interval = setInterval(() => {
        seconds += 1;
        console.log(seconds);   
    }, 1000);

    onDestroy(()=>{
        console.log('onDestroy');
        clearInterval(interval)
    })
    onMount(()=>{
        return () => {
            console.log('onMount');
        }
    })
</script>

<p>{seconds}</p>

8 - 4. 라이프 사이클 모듈화

// 기존 자식 컴포넌트 
<script>
    import { onInterval } from './utils.js'
    let seconds = 0;

    onInterval(()=> seconds += 1, 1000 )
</script>

<p>{seconds}</p>

// utils.js 파일 생성
import { onDestroy } from 'svelte'

function onInterval(seconds, ms){
    const interval = setInterval(seconds, ms);
    onDestroy(() => {
        console.log('onDestroy');
        clearInterval(interval)
    })
}

export { onInterval}

8 - 5. beforeUpdate와 afterUpdate

<script>
	import { onMount, afterUpdate, beforeUpdate } from 'svelte'
	let number = 0
	let p;

	beforeUpdate(()=>{
		console.log("beforeUpdate", p&&p.innerText);
	})
	onMount(()=>{
		console.log("onMount" );
	})
	afterUpdate(()=>{
		console.log("afterUpdate", p.innerText);
	})

</script>

<button on:click={()=> number += 1}>Add</button>
<p bind:this={p}>{number}</p>

8 - 6. tick

<script>
	import {tick} from 'svelts'
	let text = 'what the fuck!!!!!!'

	async function handleKeyDown(event){
		if(event.which !== 9) return;

		const { selectionStart, selectionEnd, value } = this;
		const selection = value.slice(selectionStart, selectionEnd)

		// 만약 소문자면 대문자로 , 대문자면 소문자로
		const replacement = /a-z/.test(selection)
			? selection.toUpperCase()
			: selection.toLowerCase();

		// 선택하지 않은 값은 그대로 + 선택한 값은 소문자 || 대문자로 + 선택하지 않고 남은부부분 그대로 
		text = (value.slice(0, selectionStart) + replacement + value.slice(selectionEnd))

		await tick(); 
		// 스크롤한 부분 유지시켜주기 위한 
		this.selectionStart = selectionStart
		this.selectionEnd = selectionEnd

	}
</script>

<textarea bind:value={text} on:keydown|preventDefault={handleKeyDown}></textarea>