В статье мы поговорим как можно реализовать компонент — модальное окно на Vue js, который можно переиспользовать в любом месте вашего проекта и динамически подставлять данные в компонент используя слоты.
Структура модального окна
Для начала определим структуру. Внутри модального окна будет четыре главных блока — заголовок (.modal-title), контент (.modal-content), футер (.modal-footer) и кнопка закрытия. Плюс нам понадобится обертка с затемнением на весь экран чтобы при нажатии на нее окно закрывалось. В основном компоненте модального окна мы будем использовать именованные слоты. Они позволят нам передавать блоки в основной компонент. Подробнее о слотах во Vue js можно почитать здесь.
Назовем основной компонент modal-window.vue и выглядеть он будет так:
<template>
<div v-if="show" class="modal-shadow" @click.self="closeModal">
<div class="modal">
<div class="modal-close" @click="closeModal">✖</div>
<slot name="title">
<h3 class="modal-title">Заголовок</h3>
</slot>
<slot name="body">
<div class="modal-content">
Дефолтный контент модального окна
</div>
</slot>
<slot name="footer">
<div class="modal-footer">
<button class="modal-footer__button" @click="closeModal">
Ок
</button>
</div>
</slot>
</div>
</div>
</template>
<script>
export default {
name: "ModalWindow",
data: function () {
return {
show: false
}
},
methods: {
closeModal: function () {
this.show = false
}
}
}
</script>
<style scoped
lang="scss">
.modal-shadow {
position: absolute;
top: 0;
left: 0;
min-height: 100%;
width: 100%;
background: rgba(0, 0, 0, 0.39);
}
.modal {
background: #fff;
border-radius: 8px;
padding: 15px;
min-width: 420px;
max-width: 480px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
&-close {
border-radius: 50%;
color: #fff;
background: #2a4cc7;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 7px;
right: 7px;
width: 30px;
height: 30px;
cursor: pointer;
}
&-title {
color: #0971c7;
}
&-content {
margin-bottom: 20px
}
&-footer {
&__button {
background-color: #0971c7;
color: #fff;
border: none;
text-align: center;
padding: 8px;
font-size: 17px;
font-weight: 500;
border-radius: 8px;
min-width: 150px;
}
}
}
</style> Мы используем директиву v-show чтобы показывать и скрывать модальное окно в зависимости от значения параметра show. Подробнее о директивах тут. Так же у нас есть функция closeModal — функция меняет значение show на false и окно закрывается. Срабатывает функция при клике на кнопку закрытия модального окна и на клик по серой подложке. При добавлении обработчика события клика на элемент подложки мы использовали модификатор события self, который говорит о том, что клик будет срабатывать только на этом элементе и не будет происходить погружение события. В тэги <slot></slot> мы поместили контент, который будет отображаться по умолчанию если мы ничего не передадим в слот.
Использование модального окна с данными по умолчанию
Теперь создадим компонент app.vue и вызовем модальное окно, которое у нас получилось.
<template>
<div class="page">
<button сlass="show-modal-button" @click="showModal">Показать модальное окно</button>
<modal-window ref="modal"></modal-window>
</div>
</template>
<script>
import ModalWindow from '../modal-window/modal-window.vue'
export default {
name: 'App',
components: {
ModalWindow
},
methods: {
showModal: function () {
this.$refs.modal.show = true
}
},
}
</script>
<style lang="scss">
body {
font-family: 'Source Sans Pro', sans-serif;
margin: 0;
padding: 0;
height: 100vh;
}
.page {
position: relative;
width: 100%;
min-height: 100%;
}
</style> Для этого мы импортировали компонент ModalWindow в компонент App и задали ему ref="modal", чтобы в последующем мы могли обратиться к модальному окну. При нажатии на кнопку .show-modal-button выполняется функция showModal. В этой функции мы обращаемся к компоненту модального окна <modal-window></modal-window> через this.$refs.modal и меняем значение свойства show на true. У нас получился такой результат.
Передаем данные в слоты
Теперь посмотрим как мы можем переиспользовать модальное окно с другими данными. Для этого создадим еще одну кнопку и еще одно модальное окно. После изменений компонент App будет выглядеть следующим образом.
<template>
<div class="page">
<button class="show-modal-button" @click="showModal">Показать модальное окно</button>
<button class="show-modal-button" @click="showModalTwo">Показать еще одно модальное окно</button>
<modal-window ref="modal"></modal-window>
<modal-window ref="modalTwo">
<template v-slot:title>
<h3 class="modal-title">Добавить отзыв</h3>
</template>
<template v-slot:body>
<textarea class="modal-textarea"
placeholder="Добавьте отзыв">
</textarea>
</template>
<template v-slot:footer>
<button class="modal-footer__button" @click="sendDataFunction">Отправить</button>
</template>
</modal-window>
</div>
</template>
<script>
import ModalWindow from '../modal-window/modal-window.vue'
export default {
name: 'App',
components: {
ModalWindow
},
methods: {
showModal: function () {
this.$refs.modal.show = true
},
showModalTwo: function () {
this.$refs.modalTwo.show = true
},
sendDataFunction: function () {
// обработчик отправки данных
}
},
}
</script>
Во втором модальном окне мы передаем в слоты с помощью тега <template> и атрибута v-slot:slotname другие данные. Таким образом блок <h3 class="modal-title">Добавить отзыв</h3> заменит блок по умолчанию, который указан в компоненте модального окна. В слот footer мы передали кнопку, при клике на которую мы можем, например, отправить данные из textarea, которую мы передали в слот body.
Добавляем анимацию
Нашему компоненту не хватает немного изящности. Для этого добавим анимацию модального окна. Вернемся к исходному компоненту и добавим специальный компонент-обертку <transiton></transition>. Дадим ему атрибут name="modal". Компонент transition отвечает за анимацию переходов добавляя классы. Эти классы мы и будем использовать для стилизации анимации. Подробнее об этих классах можно почитать по ссылке.
<template>
<transition name="modal">
<div v-if="show" class="modal-shadow" @click.self="closeModal">
<div class="modal">
<div class="modal-close" @click="closeModal">✖</div>
<slot name="title">
<h3 class="modal-title">Заголовок</h3>
</slot>
<slot name="body">
<div class="modal-content">
Дефолтный контент модального окна
</div>
</slot>
<slot name="footer">
<div class="modal-footer">
<button class="modal-footer__button" @click="closeModal">
Ок
</button>
</div>
</slot>
</div>
</div>
</transition>
</template>
<script>
export default {
name: "ModalWindow",
data: function () {
return {
show: false
}
},
methods: {
closeModal: function () {
this.show = false
}
}
}
</script>
<style lang="scss">
...
.modal-enter-active, .modal-leave-active {
transition: opacity 2s
}
.modal-enter, .modal-leave-to {
opacity: 0
}
...
</style> В итоге у нас получилась такая анимация появления модального окна:
Из статьи мы узнали как написать переиспользуемый компонент — модальное окно на Vue js с использованием слотов и стандартного компонента анимации.