Вступление
Диалог подтверждения - это шаблон пользовательского интерфейса, в котором пользователю будет предоставлен выбор: продолжить действие или отменить его. Он обычно используется с деструктивными или необратимыми действиями, чтобы убедиться, что пользователь действительно захочет продолжить.
В этой статье мы реализуем многоразовый модульный диалог подтверждения на Vue.js.
Создание многоразового всплывающего компонента
Начнем с создания многоразового базового компонента для любых всплывающих компонентов. Таким образом, нам не нужно повторно реализовывать механику всплывающих окон несколько раз. Позже это можно будет повторно использовать для создания чего угодно, от окна предупреждения до всплывающего окна информационного бюллетеня.
Начнем с шаблона:
<!-- components/PopupModal.vue -->
<template>
<transition name="fade">
<div class="popup-modal" v-if="isVisible">
<div class="window">
<slot></slot>
</div>
</div>
</transition>
</template>
Обратите внимание, что мы добавили в шаблон <slot></slot>
Этот тег
позволяет нам вставлять любое содержимое в элемент PopupModal
<slot></slot>
. Чтобы узнать больше о том, как работают слоты, см.
Руководство Vue по
слотам .
Мы также добавили в шаблон тег <transition name="fade">
Мы будем
использовать это в следующем разделе, чтобы анимировать эффект
постепенного появления / исчезновения в диалоге.
Затем мы добавим функции событий data()
, open()
и close()
<!-- components/PopupModal.vue -->
<script>
export default {
name: 'PopupModal',
data: () => ({
isVisible: false,
}),
methods: {
open() {
this.isVisible = true
},
close() {
this.isVisible = false
},
},
}
</script>
И, наконец, добавим к нему немного стиля:
<!-- components/PopupModal.vue -->
<style scoped>
/* css class for the transition */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.popup-modal {
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 0.5rem;
display: flex;
align-items: center;
z-index: 1;
}
.window {
background: #fff;
border-radius: 5px;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.2);
max-width: 480px;
margin-left: auto;
margin-right: auto;
padding: 1rem;
}
</style>
Подтверждение диалогового окна анимации
В теге шаблона вы увидите тег <transition name="fade">
. Это
используется для анимации простых состояний входа / выхода. Все, что
находится внутри этого тега, будет анимировано, если оно было добавлено
или удалено из тега.
Мы используем условное v-if="isVisible"
чтобы скрыть и показать
всплывающее окно. Вы можете узнать больше об этом в руководстве Vue по
переходам .
Чтобы указать, как контент перемещается, мы назвали нашу анимацию fade
. Чтобы реализовать этот переход в CSS, мы добавим классы с префиксом
name fade
, соответствующие нашему атрибуту name
тега <transition>
Все, что он делает, это анимирует непрозрачность при закрытии и открытии всплывающего окна:
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
Наследование компонента Popup
Чтобы создать наш диалог подтверждения, мы унаследуем PopupModal
по
составу и настроим модальное окно многократного использования, чтобы оно
стало диалоговым окном подтверждения.
Создадим новый файл components/ConfirmDialogue.vue
и определим внутри
него шаблон:
<!-- components/ConfirmDialogue.vue -->
<template>
<popup-modal ref="popup">
<h2 style="margin-top: 0">{{ title }}</h2>
<p>{{ message }}</p>
<div class="btns">
<button class="cancel-btn" @click="_cancel">{{ cancelButton }}</button>
<span class="ok-btn" @click="_confirm">{{ okButton }}</span>
</div>
</popup-modal>
</template>
Поскольку мы определили <slot></slot>
в popup-modal
компоненте, все,
что мы помещаем между его тегами компонента (<
popup-modal></popup-modal>
), будет отображаться между его тегами
<slot>
.
Мы также добавили ref="popup"
к тегу popup-modal
Установив этот
атрибут, мы теперь можем получить доступ к popup-modal
с помощью
this.$refs.popup
. Мы будем использовать эту ссылку для вызова
open()
и close()
во всплывающем модальном окне.
Затем давайте реализуем методы из родительского компонента:
<!-- components/ConfirmDialogue.vue -->
<script>
import PopupModal from './PopupModal.vue'
export default {
name: 'ConfirmDialogue',
components: { PopupModal },
data: () => ({
// Parameters that change depending on the type of dialogue
title: undefined,
message: undefined, // Main text content
okButton: undefined, // Text for confirm button; leave it empty because we don't know what we're using it for
cancelButton: 'Go Back', // text for cancel button
// Private variables
resolvePromise: undefined,
rejectPromise: undefined,
}),
methods: {
show(opts = {}) {
this.title = opts.title
this.message = opts.message
this.okButton = opts.okButton
if (opts.cancelButton) {
this.cancelButton = opts.cancelButton
}
// Once we set our config, we tell the popup modal to open
this.$refs.popup.open()
// Return promise so the caller can get results
return new Promise((resolve, reject) => {
this.resolvePromise = resolve
this.rejectPromise = reject
})
},
_confirm() {
this.$refs.popup.close()
this.resolvePromise(true)
},
_cancel() {
this.$refs.popup.close()
this.resolvePromise(false)
// Or you can throw an error
// this.rejectPromise(new Error('User cancelled the dialogue'))
},
},
}
</script>
Наконец, давайте добавим к нему немного стиля, чтобы он выглядел немного лучше:
<!-- components/ConfirmDialogue.vue -->
<style scoped>
.btns {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.ok-btn {
color: red;
text-decoration: underline;
line-height: 2.5rem;
cursor: pointer;
}
.cancel-btn {
padding: 0.5em 1em;
background-color: #d5eae7;
color: #35907f;
border: 2px solid #0ec5a4;
border-radius: 5px;
font-weight: bold;
font-size: 16px;
text-transform: uppercase;
cursor: pointer;
}
</style>
Использование диалога подтверждения
Чтобы использовать диалог подтверждения, вам нужно включить только
components/ConfirmDialogue.vue
. Например, давайте создадим страницу с
кнопкой «Удалить», которая проверяет, действительно ли вы хотите
удалить другую страницу:
<template>
<div>
<h1>Delete Page</h1>
<button class="delete-btn" @click="doDelete">Delete Page</button>
<confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
</div>
</template>
<script>
import ConfirmDialogue from '../components/ConfirmDialogue.vue'
export default {
components: { ConfirmDialogue },
methods: {
async doDelete() {
const ok = await this.$refs.confirmDialogue.show({
title: 'Delete Page',
message: 'Are you sure you want to delete this page? It cannot be undone.',
okButton: 'Delete Forever',
})
// If you throw an error, the method will terminate here unless you surround it wil try/catch
if (ok) {
alert('You have successfully delete this page.')
} else {
alert('You chose not to delete this page. Doing nothing now.')
}
},
},
}
</script>
<style scoped>
.delete-btn {
padding: 0.5em 1em;
background-color: #eccfc9;
color: #c5391a;
border: 2px solid #ea3f1b;
border-radius: 5px;
font-weight: bold;
font-size: 16px;
text-transform: uppercase;
cursor: pointer;
}
</style>
Поскольку мы используем await
в нашем методе, чтобы получить результат
диалога подтверждения, нам нужно добавить async
в определение нашего
метода.
В качестве альтернативы вы можете предпочесть подход в стиле обещания:
this.$refs.confirmDialogue.show({
title: 'Delete Page',
message: 'Are you sure you want to delete this page? It cannot be undone.',
okButton: 'Delete Forever',
}).then((result) => {
if (ok) {
alert('You have successfully delete this page.')
} else {
alert('You chose not to delete this page. Doing nothing now.')
}
})
Чтобы понять, почему мы предложили выдавать ошибку, если пользователь отменяет диалог подтверждения, посмотрите, насколько плавным является следующий код:
await this.$refs.confirmDialogue.show({
title: 'Delete Page',
message: 'Are you sure you want to delete this page? It cannot be undone.',
okButton: 'Delete Forever',
})
alert('Deleting this page.')
Поскольку для отмены не требуется никаких действий, нет необходимости
обрабатывать это состояние вообще. И если вы решите обработать запрос на
отмену, просто оберните этот код с помощью try/catch
.
Заключение
В этой статье мы определили многоразовый компонент модального всплывающего окна в Vue.js и унаследовали его для реализации диалогового окна подтверждения. Затем мы добавили к нему анимацию в эстетических целях и запустили несколько примеров того, как использовать компонент, чтобы запрашивать у пользователей ввод.