-
[Svelte] Svelte 기초 - Svelte로 Modal 만들기 (Conditional Styles / Event Forwarding / Props / Event Modifiers / Slots)Frontend 2020. 6. 21. 20:27
이제 Svelte로 Modal을 만들어보려고 한다.Modal 컴포넌트를 만들어서 해당 컴포넌트를 특정 condition에 의해 보여줬다 안보여줬다 toggle할 수 있도록 만들어야 할 것이다.
일단 App.svelte와 동일한 폴더에 Modal.svelte를 만들어보자.
Conditional Styles
<script> let showModal = true; let isPromo = true; </script> {#if showModal} <div class="backdrop" class:promo={isPromo}> <div class="modal"> <p>Sign up for offers!</p> </div> </div> {/if} <style> .backdrop { width: 100%; height: 100%; position: fixed; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); } .modal { padding: 10px; border-radius: 10px; max-width: 400px; margin: 10% auto; text-align: center; background: white; } .promo .modal { background: crimson; color: white; } </style>
위 Modal 컴포넌트는 showModal 변수가 true일 때만 보여진다.
그리고 class:promo={ isPromo }는 바로 conditional styles이다.
즉, 조건에 따라 class name을 줬다 안줬다 할 수 있는 기능이다.
class:promo={ isPromo } 라고 하면 isPromo 변수가 true일 때만 promo라는 class name이 적용된다는 뜻이다.
(물론 isPromo 대신 다른 조건문이 들어갈 수도 있다.)
Props
<script> export let message = 'default value'; export let showModal = true; export let isPromo = true; </script> {#if showModal} <div class="backdrop" class:promo={isPromo}> <div class="modal"> <p>{message}</p> </div> </div> {/if}
아까의 Modal은 내부 텍스트가 정적인 텍스트로 정해져 있었기 때문에 재사용이 불가능했다.
그러나 message 변수를 만들고 이 message를 출력하게 함으로써 재사용 가능한 모달 컴포넌트로 만들 수 있다.
물론 이 message는 외부에서 주입할 수 있어야할 것이다.
변수 선언 앞에 export를 붙이면 외부에서 주입할 수 있는 prop이 된다.
만약에 변수에 어떤 값을 할당했다면, 이 것은 default value로 작동한다.
즉, 외부에서 어떤 값도 주입하지 않았을 때 기본 값으로 적용된다.
<script> import Modal from './Modal.svelte'; let showModal = true; </script> <Modal message='I am a prop value' {showModal} />
이제 App.svelte에서 Modal 컴포넌트를 import한 후,
showModal과 message를 prop으로 전달하면 Modal 컴포넌트가 보여진다.
이는 showModal 변수를 true로 설정했기 때문이다.
이제 button을 만들고 button을 클릭했을 때만 Modal이 보여지도록 설정해보자.
Event Forwarding
<script> import Modal from './Modal.svelte'; let showModal = true; const toggleModal = () => { showModal = !showModal; }; </script> <Modal message='I am a prop value' {showModal} /> <button on:click={toggleModal}>Open Modal</button>
Modal 컴포넌트를 보여주고 안보여주고 여부는 showModal 변수에 의해서 결정되고,
이 변수는 Modal 컴포넌트에 prop으로 주입된다.
문제는 이 변수는 Modal 컴포넌트 내부에서 제어할 수 없다는 점이다.
따라서 위와 같이 App.svelte에서 showModal을 toggle하는 함수를 만들어 준 다음
Modal을 여는 버튼에 click 이벤트로 등록해주어야 한다.
문제는 button을 클릭하여 Modal 창이 열린 후 다시 Modal을 닫을 수가 없다는 점이다.
왜냐면 Modal이 화면 전체를 가려버리기 때문에 Modal 내부 요소에 다시 toggleModal 함수를 등록해주어야하기 때문이다.
이 때 사용할 수 있는 방법이 바로 Event Forwarding이다.
<script> import Modal from './Modal.svelte'; let showModal = true; const toggleModal = () => { showModal = !showModal; }; </script> <Modal message='I am a prop value' {showModal} on:click={toggleModal} /> <button on:click={toggleModal}>Open Modal</button>
일단 Modal 컴포넌트에 click 이벤트로 toggleModal 함수를 등록한다.
<script> export let message = 'default value'; export let isPromo = false; export let showModal = false; </script> {#if showModal} <div class="backdrop" class:promo={isPromo} on:click> <div class="modal"> <p>{message}</p> </div> </div> {/if}
그리고 Modal 컴포넌트 내부에서 .backdrop div에 on:click을 등록한다.
이 때 값을 넣지 않고 on:click만 적는다.
이렇게 이벤트를 등록했으나 할당된 함수가 없는 경우,
스벨트 컴포넌트는 상위의 부모 컴포넌트에 등록된 on:click 함수를 그대로 사용한다.
Event Modifiers
위 방법의 단점은 Modal 컴포넌트의 backdrop 영역(: 반투명한 검정색 영역)을 클릭했을 때만 Modal이 닫혀야하는데,
모달의 하얀 부분을 클릭해도 닫힌다는 것이다.
이 것을 해결하기 위해서는 Event modifiers를 이용하면 된다.
Event Modifiers에는 다음과 같은 6가지 종류가 있다.
- preventDefault — handler 함수를 실행하기 전에 event.preventDefault()를 호출한다.
- stopPropagation — event가 다음 element 요소로 전파되는 것을 막기 위해 event.stopPropagation()을 호출한다.
- passive — touch/wheel 이벤트에서 scrolling performance를 향상시킨다. (Svelte는 이렇게 해도 안전한 경우에 자동으로 이 기능을 추가할 것이다.)
- capture — bubbling 단계 대신 capture 단계에 handler를 트리거한다. (MDN docs)
- once — 한 번 handler가 실행되면 이벤트를 바로 삭제한다.
- self — element가 event.target인 경우에만 handler를 트리거한다.
굉장히 유용한 기능이 많다. 하여튼 위 Modal 케이스에 적용하면 딱 좋은 Event modifier가 바로 self이다.
<script> export let message = 'default value'; export let isPromo = false; export let showModal = false; </script> {#if showModal} <div class="backdrop" class:promo={isPromo} on:click|self> <div class="modal"> <p>{message}</p> </div> </div> {/if}
즉, 위와 같이 Modal 컴포넌트의 on:click 옆에 |self를 추가하면
정확히 .backdrop을 클릭했을 때만 이벤트가 트리거되어 Modal이 닫힌다.
Slots
React에서는 children이라는 개념이 있다.
즉, 버튼 컴포넌트가 있다고 했을 때, <Button></Button>이라고 사용한 후,
버튼 컴포넌트 사이에 다른 dom element를 넣으면
버튼 컴포넌트 내부에서 children을 사용한 부분에 해당 dom element가 들어가게 된다.
그리고 이는 컴포넌트를 재사용하는 데 매우 유용하게 쓰인다.
svelte에도 굉장히 비슷한 개념이 있는데, 바로 slot이다.
- Unnamed slot
App.svelte
<Modal {showModal} on:click={toggleModal}> <h3>Add a new person</h3> <form> <input type="text" placeholder="name" /> <input type="text" placeholder="hair color" /> <button>Add Person</button> </form> </Modal>
Modal.svelte
{#if showModal} <div class="backdrop" class:promo={isPromo} on:click|self> <div class="modal"> <slot /> </div> </div> {/if}
위와 같이 Modal 사이에 넣은 dom들은 Modal 컴포넌트에서 <slot />이라고 선언된 자리에 들어간다.
- Named slot
App.svelte
<Modal {showModal} on:click={toggleModal}> <form> <input type="text" placeholder="name" /> <input type="text" placeholder="hair color" /> <button>Add Person</button> </form> <div slot="title"> <h3>Add a new person</h3> </div> </Modal>
Modal.svelte
{#if showModal} <div class="backdrop" class:promo={isPromo} on:click|self> <div class="modal"> <slot name="title" /> <slot /> </div> </div> {/if}
named slot은 dom에 slot이라는 속성을 추가한 다음 값을 할당해준 경우를 말한다.
slot으로 지정해 준 string이 바로 slot의 name이 된다.
이 slot은 <slot name="slot-name" />과 같은 형태로 사용된다.
반응형'Frontend' 카테고리의 다른 글
알아두면 유용한 TypeScript의 Utility type 정리 (252) 2020.06.21 [Svelte] Svelte 기초 - Svelte로 Form 다루기 / Custom Event Dispatch하기 (127) 2020.06.21 [Svelte] Svelte 기초 - Loop / If-else blocks / Inline Event Handlers (252) 2020.06.21 [Svelte] Svelte 기초 - Data binding과 Reactive Values (252) 2020.06.02 [Svelte] Svelte 첫 시작 - Setting up a Svelte App (253) 2020.06.01 COMMENT