Простой способ создания и переиспользования модальных диалогов во Vue 3

от автора

Модальные диалоги не такая и сложная задача в разработке. Разве что можно легко запутаться в коде, если нужно вызывать модальные окна по цепочке. Но это очень монотонная и неинтересная работа с повторяющейся логикой, которую подчас копируют из компонента в компонет с незначительными изменениями.

Хорошим решением было бы создать функцию, которая принимала бы компонент диалога и управляла бы его рендерингом в шаблоне, а этот диалог можно «промисифицировать» и работать с ним как с асинхронной функцией. Как например в этой библиотеке vue-modal-dialogs. К сожалению она давно не обновлялась и не опддерживает Vue 3.

А еще отличной идеей для организации кода было бы отдельные хуки, код в которых выполнялся бы в зависимости от действий пользователя.

Все это есть в плагине vuejs-confirm-dialog, о котором я хочу вам рассказать.

Конечный результат можно посмотреть в песочнице. Код немного отличается от того, что в посте.

Установка

Начнем с создания нового проекта на Vue 3. Введем в консоли:

vue create dialogs-guide  // Pick a second option  ? Please pick a preset:   Default ([Vue 2] babel, eslint) > Default (Vue 3) ([Vue 3] babel, eslint)   Manually select features

Мы получили стандартный шаблон нового проекта. Далее установим библиотеку согласно документации в README.md.

npm i vuejs-confirm-dialog

Заменим код в main.js на такой:

import { createApp } from 'vue' import App from './App.vue' import * as ConfirmDialog from 'vuejs-confirm-dialog'  createApp(App).use(ConfirmDialog).mount('#app')

Использование

Теперь перейдем в файл App.vue. Первым делом исправим код шаблона. Для работы библиотеки нам обязательно нужно добавить в него компонент <DialogsWrapper /> и удалим HelloWord:

<template>   <img alt="Vue logo" src="./assets/logo.png">   <DialogsWrapper /> </template>

Теперь изучим как использовать функцию createConfirmDialog. Используем новый синтаксис setup для раздела script. createConfirmDialog первым аргументом принимает компонент, который будет использоваться как модальное окно, а вторым входные данные для него. Функция возвращает обьект с методами для работы с модальным окном, так метод reveal вызывает диалог, а хук onConfirm принимает код, который выполниться если пользователь нажмет на «согласен». Можно заставить появляться компонерт HelloWord при нажатии на лого и передать ему значение пропса msg:

// App.vue <template>   <img alt="Vue logo" src="./assets/logo.png" @click="reveal">   <DialogsWrapper /> </template>  <script setup> import HelloWorld from './components/HelloWorld.vue' import { createConfirmDialog } from 'vuejs-confirm-dialog'  const { reveal } = createConfirmDialog(HelloWorld, { msg: 'Hi!'}) </script>

Никакой дополнительной логики не требуется. Компонент отрисовывается после вызова функции reveal и исчезает после того, как пользователь отреагирует на диалог.

Реальный пример компонента

Теперь напишем что-то более приближенное к реальному использованию.

Создадим новый компонент SimpleDialog.vue в папке components:

<template>     <div class="modal-container">         <div class="modal-body">             <span class="modal-close" @click="emit('cancel')">?</span>             <h2>{{ question }}</h2>             <div class="modal-action">                 <button class="modal-button" @click="emit('confirm')">Confirm</button>                 <button class="modal-button" @click="emit('cancel')">Cancel</button>             </div>         </div>     </div> </template>  <script setup>     import { defineProps, defineEmits } from 'vue'      const props = defineProps(['question'])     const emit = defineEmits(['confirm', 'cancel']) </script>  <style>     .modal-container {         display: flex;         justify-content: center;         align-items: center;         position: absolute;         top: 0;         left: 0;         right: 0;         width: 100%;         height: 100%;         background-color: #cececeb5;     }     .modal-body {         background-color: #fff;         border: 2px solid #74a2cf;         border-radius: 10px;         text-align: center;         padding: 20px 40px;         min-width: 250px;         display: flex;         flex-direction: column;     }     .modal-action {         display: flex;         flex-direction: row;         gap: 40px;         justify-content: center;     }     .modal-button {         cursor: pointer;         height: 30px;         padding: 0 25px;         border: 2px solid #74a2cf;         border-radius: 5px;         background-color: #80b2e4;         color: #fff;     }     .modal-close {     cursor: pointer;         position: relative;         align-self: end;         right: -33px;         top: -17px;     } </style> 

Обратите внимание, что для полноценной работы нужно добавить два входящих события в модальный диалог: ['confirm', 'cancel'].

А теперь используем его для подтверждения какого-либо действия, например, чтобы спрятать логотип. Логику кода, который будет исполняться после согласия пользователя, поместим в коллбек хука onConfirm.

<template>   <img v-show="showLogo" alt="Vue logo" src="./assets/logo.png">   <button @click="reveal">Hide Logo</button>    <DialogsWrapper /> </template>  <script setup> import SimpleDialog from './components/SimpleDialog.vue' import { createConfirmDialog } from 'vuejs-confirm-dialog' import { ref } from 'vue'  const showLogo = ref(true)  const { reveal, onConfirm } = createConfirmDialog(SimpleDialog, { question: 'Are you sure you want to hide the logo?'})  onConfirm(() => {   showLogo.value = false }) </script>

Переиспользование

Что делать, если у нас есть много случаев, когда требуется подтверждение каких-либо действий? Не писать же каждый раз createConfirmDialog заново?

Можно написать функцию, которая автоматизирует этот процесс для нас.

// src/composables/useConfirmBeforeAction.js  import SimpleDialog from './../components/SimpleDialog' import { createConfirmDialog } from 'vuejs-confirm-dialog'  const useConfirmBeforeAction = (action, props) => {   const { reveal, onConfirm } = createConfirmDialog(SimpleDialog, props)    onConfirm(action)    reveal() }  export default useConfirmBeforeAction

Теперь используем ее для подтверждения перехода по внешним ссылкам:

// App.vue  <template>     <ul>         <li v-for="(link, i) in LINKS" @click="goToLink(link)" :key="i">             {{ link }}         </li>     </ul>     <DialogsWrapper /> </template>  <script setup>     import SimpleDialog from './components/SimpleDialog.vue'     import { createConfirmDialog } from 'vuejs-confirm-dialog'     import useConfirmBeforeAction from './composables/useConfirmBeforeAction'      const LINKS = [         'https://vuejs.org/',         'https://github.com/',         'https://vueuse.org/',     ]      const goToLink = (link) => {         useConfirmBeforeAction(             () => {                 window.location = link             },             { question: `Do you want to go to ${link}?` }         )     }  </script>

Заключение

Функция createConfirmDialog позволяет упростить работу с модальными окнами, переиспользование логики и создание цепочек последовательных диалогов. Она берет на себе заботу о рендере модального окна, передачу входящих параметров в компонент и получении данныт от него. Она очень гибкая — ее очень легко подстроить под ваши нужды.

Это не все ее возможности. Например, если концеция хуков вам не близка, можно заменить их на работу с промисом, который возвращает функция reveal. И даже использовать её в Options API.


ссылка на оригинал статьи https://habr.com/ru/post/662652/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *