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
1 2 3 4 |
<span class="token operator"><</span>Teleport <span class="token operator">:</span>disabled<span class="token operator">=</span><span class="token string">"true"</span><span class="token operator">></span> <span class="token operator">...</span> <span class="token operator"><</span><span class="token operator">/</span>Teleport<span class="token operator">></span> |
Example:
- Ví dụ làm một modal được viết băng vue 3, ts, tailwind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<span class="token operator"><</span>script setup lang<span class="token operator">=</span><span class="token string">"ts"</span><span class="token operator">></span> <span class="token keyword">import</span> <span class="token punctuation">{</span> ref <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> CloseIcon <span class="token keyword">from</span> <span class="token string">'@/components/icons/CloseIcon.vue'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> useOnClickOutside <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/composable/click-outside'</span><span class="token punctuation">;</span> type TProps <span class="token operator">=</span> <span class="token punctuation">{</span> show<span class="token operator">:</span> boolean<span class="token punctuation">;</span> title<span class="token operator">:</span> string<span class="token punctuation">;</span> textSubmit<span class="token operator">?</span><span class="token operator">:</span> string<span class="token punctuation">;</span> clickToClose<span class="token operator">?</span><span class="token operator">:</span> boolean<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> props <span class="token operator">=</span> <span class="token function">withDefaults</span><span class="token punctuation">(</span>defineProps<span class="token operator"><</span>TProps<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> clickToClose<span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// cú pháp trong vue 3.3</span> <span class="token keyword">const</span> emit <span class="token operator">=</span> defineEmits<span class="token operator"><</span><span class="token punctuation">{</span> onSubmit<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> onClose<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> modalRef <span class="token operator">=</span> ref<span class="token operator"><</span>HTMLElement <span class="token operator">|</span> <span class="token keyword">null</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">useOnClickOutside</span><span class="token punctuation">(</span>modalRef<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>props<span class="token punctuation">.</span>clickToClose<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token function">emit</span><span class="token punctuation">(</span><span class="token string">'onClose'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> <span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>Teleport to<span class="token operator">=</span><span class="token string">"body"</span><span class="token operator">></span> <span class="token operator"><</span>transition name<span class="token operator">=</span><span class="token string">"fade"</span><span class="token operator">></span> <span class="token operator"><</span>div v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"show"</span><span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"fixed inset-0 bg-black opacity-30 cursor-pointer"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"fixed inset-0 flex items-center justify-center z-[999]"</span><span class="token operator">></span> <span class="token operator"><</span>div ref<span class="token operator">=</span><span class="token string">"modalRef"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"bg-white border-gray-200 w-[342px] md:w-[500px] lg:w-[640px] rounded-lg relative"</span> <span class="token operator">></span> <span class="token operator"><</span>CloseIcon <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"absolute top-[27.5px] right-6 hover:cursor-pointer z-10"</span> @click<span class="token operator">=</span><span class="token string">"emit('onClose')"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"divide-y divide-gray-200"</span><span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"p-6 text-[#111928] text-lg leading-[27px] font-semibold"</span> <span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span><span class="token punctuation">{</span><span class="token punctuation">{</span> title <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"p-6"</span><span class="token operator">></span> <span class="token operator"><</span>slot name<span class="token operator">=</span><span class="token string">"content"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token comment">// check dùng cho modal ko cần nút bấm</span> <span class="token operator"><</span>div v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"!!textSubmit"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"p-6 flex justify-center"</span><span class="token operator">></span> <span class="token operator"><</span>button @click<span class="token operator">=</span><span class="token string">"emit('onSubmit')"</span> <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"bg-[#1C64F2] px-4 py-2.5 rounded-lg text-white text-sm font-medium leading-[21px]"</span> <span class="token operator">></span> <span class="token punctuation">{</span><span class="token punctuation">{</span> textSubmit <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>transition<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>Teleport<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> |
- 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<span class="token keyword">import</span> type <span class="token punctuation">{</span> Ref <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> useEventListener <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./use-event-listener'</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">useOnClickOutside</span><span class="token punctuation">(</span> rootEl<span class="token operator">:</span> Ref<span class="token operator"><</span>HTMLElement <span class="token operator">|</span> <span class="token keyword">null</span><span class="token operator">></span><span class="token punctuation">,</span> <span class="token function-variable function">callback</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> any <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// `mousedown` or `mouseup` here makes it easier to not trigger the callback immedialty</span> <span class="token comment">// if you want to use `click` you need to call `stopPropagation` on the trigger element.</span> <span class="token function">useEventListener</span><span class="token punctuation">(</span>window<span class="token punctuation">,</span> <span class="token string">'mouseup'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e<span class="token operator">:</span> Event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> clickedEl <span class="token operator">=</span> e<span class="token punctuation">.</span>target <span class="token keyword">as</span> HTMLElement<span class="token punctuation">;</span> <span class="token comment">// skip if the root element contains the clicked element</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>rootEl<span class="token punctuation">.</span>value<span class="token operator">?.</span><span class="token function">contains</span><span class="token punctuation">(</span>clickedEl<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// otherwise execute the action</span> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> onMounted<span class="token punctuation">,</span> onBeforeUnmount <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span><span class="token punctuation">;</span> <span class="token comment">// use-event-listener.ts</span> <span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">useEventListener</span><span class="token punctuation">(</span> target<span class="token operator">:</span> EventTarget<span class="token punctuation">,</span> event<span class="token operator">:</span> string<span class="token punctuation">,</span> <span class="token function-variable function">handler</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">e<span class="token operator">:</span> Event</span><span class="token punctuation">)</span> <span class="token operator">=></span> any <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">onMounted</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> target<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span>event<span class="token punctuation">,</span> handler<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// clean it up</span> <span class="token function">onBeforeUnmount</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> target<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span>event<span class="token punctuation">,</span> handler<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
- 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<span class="token operator"><</span>script setup lang<span class="token operator">=</span><span class="token string">"ts"</span><span class="token operator">></span> <span class="token comment">// import RootModal from ''</span> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> <span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>RootModal <span class="token operator">:</span>show<span class="token operator">=</span><span class="token string">"isShow"</span> <span class="token operator">:</span>title<span class="token operator">=</span><span class="token string">"''Nhap title"</span> <span class="token operator">:</span>text<span class="token operator">-</span>submit<span class="token operator">=</span><span class="token string">"'text ......'"</span> @on<span class="token operator">-</span>close<span class="token operator">=</span><span class="token string">"emit('onClose')"</span> @on<span class="token operator">-</span>submit<span class="token operator">=</span><span class="token string">"emit('onSubmit')"</span> <span class="token operator">></span> <span class="token operator"><</span>template #content<span class="token operator">></span> <span class="token comment">// nội dung</span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>RootModal<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> |