Teleport in Vue 3
- Tram Ho
This article introduces Teleport vue 3 and the same simple modal function
What is teleport in vue 3?
- <Teleport> is a built-in component in Vue 3 that allows us to “teleport” (transpose) part of a component’s template to a DOM node that exists outside of the component’s DOM structure.
- It has a prop : to and a default slot which shows the position content to be displayed in the DOM element mentioned in prop to.
- Disabling Teleport : use 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:
- Example of making a modal written in 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> |
- <script setup> section: This is a new feature in Vue 3 that allows you to write component logic without having to declare variables and functions using concise declarations like ref, defineProps, and defineEmits.
- Import: The line import { ref } from ‘vue’; import ref from the Vue library to create a ref for the modal element.
- Import CloseIcon : import the CloseIcon component svg file modal close button.
- Import the useOnClickOutside function useOnClickOutside from a composable (composable is a logical reuse in Vue) defined in the file click-outside.ts. This function allows us to handle the click event outside of the modal element.
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 | <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: Line type TPProps = { … }; defines a style for the props received in the modal element. Props include show (which controls modal display), title (modal title), textSubmit (submit button text), and clickToClose (whether or not to allow modal to be closed on click outside).
- Define Default Props: use defineProps to define props and withDefaults to set default values for props if none are provided.
- Define Emits: use defineEmits to define the events this component can emit, in this case onSubmit and onClose.
- Modal Ref: const modalRef = ref<HTMLElement | null>(null); create a ref for the modal element, which will be used in useOnClickOutside to identify the element outside the modal on click.
- useOnClickOutside(modalRef, () => { … }); use composable useOnClickOutside to handle the click event outside the modal element. When external click and clickToClose is enabled, the onClose event is fired.
Use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <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> |