ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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" />과 같은 형태로 사용된다.

     

    반응형

    COMMENT