+1

Teleport trong vue 3

Bài viết này giới thiệu Teleport vue 3 và cùng là một chức năng modal đơn giản ✌️

Teleport trong vue 3 là gì?

  • <Teleport> là một thành phần tích hợp sẵn trong Vue 3 cho phép chúng ta "teleport" (chuyển đổi vị trí) một phần của template của một thành phần vào một nút DOM tồn tại ngoài cấu trúc DOM của thành phần đó.
  • Nó có một prop : to và một default slot hiển thị nội dung vị trí sẽ hiển thị trong phần tử DOM được đề cập ở prop to.
  • Disabling Teleport : dùng prop disabled
<Teleport :disabled="true">
 ...
</Teleport>

Example:

  • Ví dụ làm một modal được viết băng vue 3, ts, tailwind
<script setup lang="ts">
import { ref } from 'vue';
import CloseIcon from '@/components/icons/CloseIcon.vue';
import { useOnClickOutside } from '@/composable/click-outside';

type TProps = {
  show: boolean;
  title: string;
  textSubmit?: string;
  clickToClose?: boolean;
};

const props = withDefaults(defineProps<TProps>(), {
  clickToClose: false
});

// cú pháp trong vue 3.3
const emit = defineEmits<{
  onSubmit: [];
  onClose: [];
}>();

const modalRef = ref<HTMLElement | null>(null);

useOnClickOutside(modalRef, () => {
  if (!props.clickToClose) return;

  emit('onClose');
});
</script>

<template>
  <Teleport to="body">
    <transition name="fade">
      <div v-if="show">
        <div class="fixed inset-0 bg-black opacity-30 cursor-pointer" />
        <div class="fixed inset-0 flex items-center justify-center z-[999]">
          <div
            ref="modalRef"
            class="bg-white border-gray-200 w-[342px] md:w-[500px] lg:w-[640px] rounded-lg relative"
          >
            <CloseIcon
              class="absolute top-[27.5px] right-6 hover:cursor-pointer z-10"
              @click="emit('onClose')"
            />
            <div class="divide-y divide-gray-200">
              <div
                class="p-6 text-[#111928] text-lg leading-[27px] font-semibold"
              >
                <p>{{ title }}</p>
              </div>
              <div class="p-6">
                <slot name="content" />
              </div>
              // check dùng cho modal ko cần nút bấm
              <div v-if="!!textSubmit" class="p-6 flex justify-center">
                <button
                  @click="emit('onSubmit')"
                  class="bg-[#1C64F2] px-4 py-2.5 rounded-lg text-white text-sm font-medium leading-[21px]"
                >
                  {{ textSubmit }}
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </transition>
  </Teleport>
</template>

  • Phần <script setup>: Đây là một tính năng mới trong Vue 3 cho phép bạn viết logic component mà không cần phải khai báo các biến và hàm bằng cách sử dụng các khai báo ngắn gọn như ref, defineProps, và defineEmits.

  • Import: Dòng import { ref } from 'vue'; import ref từ thư viện Vue để tạo một ref cho phần tử modal.

  • Import CloseIcon : import thành phần CloseIcon file svg nút đóng modal.

  • Import useOnClickOutside hàm useOnClickOutside từ một composable (composable là một cách tái sử dụng logic trong Vue) được định nghĩa trong file click-outside.ts. Chức năng này cho phép chúng ta xử lý sự kiện nhấp chuột bên ngoài phần tử modal.

import type { Ref } from 'vue';
import { useEventListener } from './use-event-listener';

export function useOnClickOutside(
  rootEl: Ref<HTMLElement | null>,
  callback: () => any
) {
  // `mousedown` or `mouseup` here makes it easier to not trigger the callback immedialty
  // if you want to use `click` you need to call `stopPropagation` on the trigger element.
  useEventListener(window, 'mouseup', (e: Event) => {
    const clickedEl = e.target as HTMLElement;

    // skip if the root element contains the clicked element
    if (rootEl.value?.contains(clickedEl)) {
      return;
    }

    // otherwise execute the action
    callback();
  });
}
    //
 import { onMounted, onBeforeUnmount } from 'vue';

    
   // use-event-listener.ts
export function useEventListener(
  target: EventTarget,
  event: string,
  handler: (e: Event) => any
) {
  onMounted(() => {
    target.addEventListener(event, handler);
  });

  // clean it up
  onBeforeUnmount(() => {
    target.removeEventListener(event, handler);
  });
}

Mình đã có giới thiệu một số composable hay dùng ở đây

  • Define Props: Dòng type TProps = { ... }; định nghĩa một kiểu cho các props nhận vào thành phần modal. Props bao gồm show (kiểm soát hiển thị modal), title (tiêu đề modal), textSubmit (văn bản nút submit), và clickToClose (có cho phép đóng modal khi nhấp chuột bên ngoài hay không).

  • Define Default Props: sử dụng defineProps để định nghĩa các props và withDefaults để thiết lập các giá trị mặc định cho các props nếu không được cung cấp.

  • Define Emits: sử dụng defineEmits để định nghĩa các sự kiện mà thành phần này có thể phát ra, trong trường hợp này là onSubmit và onClose.

  • Modal Ref: const modalRef = ref<HTMLElement | null>(null); tạo một ref cho phần tử modal, sẽ được sử dụng trong useOnClickOutside để xác định phần tử bên ngoài modal khi nhấp chuột.

  • useOnClickOutside(modalRef, () => { ... }); sử dụng composable useOnClickOutside để xử lý sự kiện nhấp chuột bên ngoài phần tử modal. Khi nhấp chuột bên ngoài và clickToClose được bật, sự kiện onClose sẽ được phát ra.

Sử dụng:

  <script setup lang="ts">
      // import RootModal from ''
   </script>
  <template>
 <RootModal
   :show="isShow"
   :title="''Nhap title"
   :text-submit="'text ......'"
   @on-close="emit('onClose')"
   @on-submit="emit('onSubmit')"
 >
   <template #content>
     // nội dung
   </template>
 </RootModal>
</template>

** TRÊN ĐÂY LÀ NHỮNG CHIA SẺ CỦA MÌNH, CẢM ƠN CÁC BẠN ĐÃ ĐỌC BÀI VIẾT .**😘


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí