В статье мы поговорим как можно реализовать компонент — модальное окно на Vue js, который можно переиспользовать в любом месте вашего проекта и динамически подставлять данные в компонент используя слоты.
Структура модального окна
Для начала определим структуру. Внутри модального окна будет четыре главных блока — заголовок (.modal-title), контент (.modal-content), футер (.modal-footer) и кнопка закрытия. Плюс нам понадобится обертка с затемнением на весь экран чтобы при нажатии на нее окно закрывалось. В основном компоненте модального окна мы будем использовать именованные слоты. Они позволят нам передавать блоки в основной компонент. Подробнее о слотах во Vue js можно почитать здесь.
Назовем основной компонент modal-window.vue
и выглядеть он будет так:
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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
<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 и вызовем модальное окно, которое у нас получилось.
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 |
<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 будет выглядеть следующим образом.
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 |
<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 отвечает за анимацию переходов добавляя классы. Эти классы мы и будем использовать для стилизации анимации. Подробнее об этих классах можно почитать по ссылке.
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 |
<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 с использованием слотов и стандартного компонента анимации.