Как сделать модальное окно vue js

Добавил пользователь Дмитрий К.
Обновлено: 11.09.2024

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

Самое главное преимущество модального окна это то, что оно показывает добавленную информацию без дополнительной загрузки страницы. Оно дает пользователю соответствующую информацию в виде диалогового окна.

Другое преимущество относится к медленной загрузке, что может привлечь внимание пользователей.

Теперь, когда мы узнали, что такое модальное окно, перейдем к его созданию.

Есть различные способы создания модального окна с помощью HTML, CSS и JavaScript.

Давайте начнем создать код шаг за шагом!

1. Создайте HTML.

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

, чтобы создать внешний вид диалогового окна. Оно будет содержать контент, который мы хотим показать пользователю.

2. Добавьте CSS.

Второй шаг - это добавление стиля к id с помощью CSS свойств.

Потом добавим стиль к внутреннему div и сделаем его горизонтальным.

Для максимальной совместимости браузера вместе со свойствами border-radius и transform используются такие расширения, как -Webkit- для Safari, Google Chrome и Opera (новые версии), -ms- для Internet Explorer, и -Moz- для Firefox.

Также установите к body свойства height, margin, padding и font-family.

3. Добавьте JavaScript.

Последним шагом является добавление JavaScript к нашему коду.

Мы можем добавить функцию внутри нашего документа или во внешнем .js файле.

Чтобы скрытый контент стал видимым, используется автоматический метод переключения.

Нам необходимо добавить следующее на нашу страницу, и таким образом, когда пользователь нажмет на ссылку, будет отображено окно.


Нам все еще не хватает компонентов создания и обновленияд данных, и в этой статье мы собираемся реализовать эти недостающие части.

Создание объекта-владельца (Owner)

Чтобы упростить чтение и понимание, мы собираемся написать этот компонент в нескольких сегментах. Начнем с шаблона.

Шаблон OwnerCreate

Во-первых, мы собираемся создать форму с тремя полями ввода. Для этого давайте создадим компонент OwnerCreate.vue в папке src/components/Owner :

В этом шаблоне есть кое что новое. В теге формы есть оператор @submit.prevent . Ранее мы узнали, что @submit - это синтаксис для прослушивания события submit . Но теперь у нас есть модификатор prevent , который эквивалентен стандартному выражению JavaScript event.preventDefault() . Таким образом, мы предотвращаем перенаправление на действие формы в собственном браузере, потому что мы хотим отправлять данные с помощью Axios.

Также обратите внимание на конструкцию v-model . Чуть позже мы вернемся к ней.

Под тегом формы мы собираемся создать кнопки Сохранить и Отменить . Первая отправит форму, а вторая перенаправит на маршрут OwnerList .

Итак, прямо под последним тегом b-form-group мы вставим этот фрагмент кода:

Кроме того, мы собираемся создать модальное окно, чтобы показать информацию, было ли наше действие успешным. Мы собираемся поместить это модальное окно перед закрывающим тегом b-container :

В продолжение добавим несколько стилей:

Далее мы реализуем логику этого шаблона.

Сценарий OwnerCreate

Пришло время объяснить директиву v-model и то, как мы используем ее для связывания этих переменных с входными данными.

Это похоже на выражение привязки данных ( :prop="value" ), которое вы могли видеть в предыдущих частях, но это привязка через v-model является двусторонней. В предыдущих частях мы только передали данные из родительского компонента в дочерний компонент с использованием синтаксиса :prop="value" , и если дочерний компонент попытается изменить это значение, появится предупреждение. потому что это запрещено. С помощью директивы v-model родительский и дочерний компоненты могут изменять это значение, и оба компонента будут уведомлены об этом изменении.

Давайте реализуем этот метод:

Нам также необходимо импортировать ссылку OwnerService :

Когда появится модальное окно, нам нужно нажать кнопку OK . Этот действие вызовет метод onAlertModalOkClick , который перенаправит пользователя на маршрут OwnerList , если ответ будет успешным успешным.

Мы собираемся отредактировать компонент OwnerCreate.vue еще раз, чтобы реализовать этот метод:

Нам все еще нужно проложить маршрут и путь к этому компоненту.

Переход к компоненту OwnerCreate

Давайте отредактируем файл src/router/index.js :

Важно разместить наш маршрут над маршрутом OwnerDetails . Если мы разместим его ниже, то при доступе к маршруту /owner/create будет запущен маршрут OwnerDetails , потому что часть маршрута create будет распознаваться как параметр :id .

И наконец, давайте изменим компонент src/components/Owner/OwnerList.vue :

Теперь давайте запустим терминал и введем команду npm run dev , чтобы проверить, все ли в порядке:


Когда мы вводим действительные данные и нажимаем кнопку Сохранить :


Редактирование объекта-владельца

Форма редактирования почти идентична форме создания. Есть только несколько отличий. У нас есть параметр :id в маршруте, и мы должны получить данные для этого владельца из серверной части и отобразить их в форме. Как только мы нажмем кнопку Сохранить , мы отправим запрос PUT на серверную часть с новыми данными для этого владельца.

Давайте создадим новый компонент OwnerUpdate.vue внутри каталога src/components/Owner . Единственная разница между этим шаблоном и шаблоном для создания владельца - это имя обработчика событий для формы отправки. В этом случае давайте просто скопируем шаблон из OwnerCreate.vue и вставим его в новый компонент и отредактируем имя обработчика событий:

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

Итак, давайте реализуем сценарий для OwnerUpdate.vue :

Также определим маршрут для этого компонента:

Давайте снова наберем команду npm run dev в терминале и посмотрим на результаты:

Когда мы нажимаем кнопку Обновить в компоненте OwnerList, мы будем перенаправлены на новый компонент, и форма будет заполнена фактическими данными:


Давайте попробуем изменить наши поля ввода и нажать кнопку Сохранить . Появится модальное окно:

Обложка: Пишем одностраничное приложение с Flask и Vue.js

Эта статья — пошаговое руководство по настройке базового CRUD-приложения с помощью Vue и Flask. Начнём с создания нового приложения Vue, используя Vue CLI, а затем перейдём к выполнению основных операций CRUD с помощью RESTful API на бэкенде под управлением Python и Flask.

  • Vue v2.5.2,
  • Vue CLI v2.9.3,
  • Node v10.3.0,
  • npm v6.1.0,
  • Flask v1.0.2,
  • Python v3.6.5.

К концу этого урока вы узнаете:

  • что такое Flask;
  • что такое Vue и как он соотносится с другими UI-библиотеками и фронтенд-фреймворками вроде Angular и React.
  • выстраивать Vue-проект, используя Vue CLI;
  • создавать и рендерить компоненты Vue в браузере;
  • создавать одностраничные приложения с компонентами Vue;
  • подключать Vue-приложение к бэкенду Flask;
  • разрабатывать RESTful API с помощью Flask;
  • стилизовать компоненты Vue с помощью Bootstrap;
  • использовать Vue Router для создания маршрутов и рендеринга компонентов.

Что такое Flask?

Flask — это простой, но мощный микро-фреймворк для Python, идеально подходящий для создания RESTful API. Как Sinatra (Ruby) и Express (Node), он минималистичен и гибок, поэтому вы можете начинать с простых проектов и при необходимости создавать более сложные приложения.

Если вы первый раз работаете с Flask, вам стоит изучить следующие ресурсы:

Что такое Vue?

Vue — это JavaScript-фреймворк с открытым исходным кодом. Используется для создания пользовательских интерфейсов. Он содержит некоторые из лучших концепций React и Angular, но по сравнению с ними он более доступен, поэтому новички могут быстро приступать к работе. Также он не уступает этим фреймворкам в мощности и предоставляет все необходимые функции для создания современных фронтенд-приложений.

1 декабря 2021 – 27 февраля 2022, Онлайн, Беcплатно

Чтобы узнать больше о Vue, а также о плюсах и минусах его использования по сравнению с Angular и React, можете посмотреть следующие статьи:

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

Настройка Flask

Начнём с создания новой директории проекта:

В директории flask-vue-crud создайте новый каталог с именем server . Затем в этом каталоге создайте и активируйте виртуальную среду разработки:

Вышеуказанные команды могут отличаться в зависимости от вашей среды разработки.

Установите Flask вместе с расширением Flask-CORS:

Добавьте файл app.py в только что созданный каталог:

Flask-CORS нужен для отправки cross-origin-запросов (запросы, исходящие из другого протокола, IP-адреса, имени домена или порта), поэтому необходимо включить общий доступ к ресурсам (CORS).

Стоит отметить, что описанная выше настройка позволяет отправлять запросы из разных источников по всем маршрутам из любого домена, протокола или порта. В продакшне должны быть разрешены только запросы из домена, на котором размещено фронтенд-приложение. Для получения дополнительной информации можете ознакомиться с документацией Flask-CORS.

Нажмите Ctrl + C , чтобы завершить работу сервера. Затем вернитесь к корневой папке проекта. Теперь перейдём к фронтенду и настроим Vue.

Настройка Vue

Для создания индивидуального темплейта проекта используем мощный интерфейс Vue CLI.

Установите его глобально:

Если вы первый раз работаете с npm, вам будет полезно почитать официальное руководство по нему.

В каталоге flask-vue-crud выполните следующую команду для инициализации нового проекта Vue под именем client с конфигом webpack:

webpack — это пакетный модуль и инструмент для сборки, используемый для создания, минимизации и объединения файлов JavaScript и других клиентских ресурсов.

Шаги создания нового проекта Vue:

  1. Vue-сборка: Runtime + Compiler.
  2. Установить vue-router? — Да.
  3. Использовать ESLint для линтинга кода? — Да.
  4. Выберите пресет ESLint — Airbnb.
  5. Настроить юнит-тесты? — Нет.
  6. Настроить тесты e2e с Nightwatch? — Нет.
  7. Запустить установку npm после создания проекта? — Да, использовать NPM.

Должно получиться следующее:

Обратите внимание на сгенерированную структуру проекта. Она может показаться большой, но по факту вы будете иметь дело только с файлами и папками в каталоге /src вместе с файлом index.html .

Файл index.html является отправной точкой данного Vue-приложения.

Также обратите внимание на элемент

с идентификатором app . Это контейнер, который Vue будет использовать для присоединения сгенерированных HTML и CSS при создании пользовательского интерфейса.

Файлы и каталоги внутри папки src :

main.js — точка входа в приложение, которая загружает и инициализирует Vue вместе с корневым компонентом.
App.vue — корневой компонент, из которого будут рендериться все остальные компоненты (отправная точка).
assets — место хранения статических ассетов вроде изображений и шрифтов.
components — место хранения UI-компонентов.
router — место определения URL-адресов и сопоставление их с компонентами.

Взгляните на файл client/src/components/HelloWorld.vue . Это компонент Single File, который разбит на три разных подраздела:

  • template: для компонентного HTML;
  • script: здесь компонентная логика реализована через JavaScript;
  • style: для стилей CSS.

default vue app

Добавьте новый компонент с именем Ping.vue в папку client/src/components :

Обновите файл client/src/router/index.js так, чтобы он отображал / в компонент Ping:

В client/src/App.vue удалите изображение из темплейта:

Чтобы соединить клиентское Vue-приложение с бэкендом на Flask, можно использовать библиотеку axios для отправки AJAX-запросов.

Начнём с установки:

Обновим раздел script компонента в Ping.vue следующим образом:

Настройка Bootstrap

Добавим Bootstrap, чтобы можно было быстро настроить стиль приложения.

Игнорируйте предупреждения для jquery и popper.js . Не добавляйте их в свой проект.

Импортируем стили Bootstrap в client/src/main.js :

Обновим раздел style в client/src/App.vue :

Убедитесь, что Bootstrap подключён корректно, используя Button и Container в компоненте Ping :


Добавим компонент Books в новый файл Books.vue :

Для избавления от хеша в URL замените mode на history , чтобы использовать history API браузера для навигации:

Добавим таблицу в стиле Bootstrap в компонент Books :

Отображаться должно следующее:


Теперь можно приступить к созданию функциональности CRUD-приложения.

Что будем создавать?

GET-маршрут

Сервер

Добавим список книг в server/app.py :

Добавим обработчик маршрута:

Клиент

После инициализации компонента вызываем метод getBooks() через хук жизненного цикла (lifecycle hook) created, который выбирает книги из только что настроенного маршрута на бэкенде.

Больше информации про Lifecycle Hook находится здесь.

В темплейте просматривается список книг с помощью директивы v-for, которая создаёт новую строку таблицы на каждой итерации. Значение индекса используется в качестве ключа (key). Затем используется директива v-if для отображения Yes или No — читал пользователь книгу или нет.


Bootstrap Vue

В следующем разделе используем компонент Modal для добавления новых книг. Для этого добавим библиотеку Bootstrap Vue, которая предоставляет набор Vue-компонентов, стилизованных с помощью HTML и CSS на основе Bootstrap.

Выбор Bootstrap Vue обоснован тем, что компонент Modal Bootstrap использует jQuery. Следует избегать совместного использования jQuery и Vue в одном проекте, поскольку последний использует Virtual Dom для обновления DOM-структуры. Другими словами, если вы используете jQuery для манипуляций с DOM, Vue об этом не узнает. По крайней мере, если вам необходимо использовать jQuery, не используйте его вместе с Vue на одних и тех же элементах DOM.

Подключим библиотеку Bootstrap Vue в файле client/src/main.js :

POST-маршрут

Сервер

Обновим существующий обработчик маршрута для обработки POST-запросов для добавления новых книг:

Запустив сервер Flask, вы можете проверить POST-маршрут на новой вкладке браузера:

Клиент

Внесём следующий modal для добавления новой книги. Начнём с HTML:

Эту часть нужно добавить непосредственно перед закрывающим тегом

. Взгляните на код, v-model — это директива, используемая для привязки входных значений обратно к стейту.

Что делает Hide-Footer , вы можете просмотреть самостоятельно в документации по Bootstrap Vue.

Обновим раздел script :

Рассмотрим, что происходит в этом фрагменте кода.

  1. addBookForm() привязывается к входным данным формы через v-model . Это называется двусторонней привязкой. Узнать об этом подробнее вы можете здесь.
  2. onSubmit() запускается, когда пользователь успешно отправляет форму. При отправке предотвращается обычное поведение браузера ( evt.preventDefault() ), закрывается modal ( this.$Refs.addBookModal.hide() ), запускается метод addBook() и очищается форма ( initForm() ).
  3. addBook() отправляет POST-запрос в /books для добавления новой книги.
  4. Остальные изменения вы можете посмотреть самостоятельно в документации Vue по мере необходимости.

Теперь компонент должен выглядеть следующим образом:

Можно выполнить проверку, попробовав добавить книгу.

Компонент Alert

Добавим новый файл с именем Alert.vue в каталог client/src/components :

Затем импортируем его в разделе script компонента Books и зарегистрируем:

Теперь можно ссылаться на новый компонент в разделе template :

Обновите браузер. Должно быть отображено следующее:


Теперь добавим фактический компонент b-alert в шаблон:


Больше информации о props находится здесь.

Добавьте message в параметр data в Books.vue :

Добавим v-if , чтобы alert отображался, только если showMessage имеет значение true :

Добавим showMessage в data :

Снова обновим addBook() , установив в showMessage значение true :

Теперь можно проверить работу.

PUT-маршрут

Сервер

Для обновлений необходимо использовать уникальный идентификатор, поскольку нельзя полагаться на то, что заголовок книги будет уникальным. Можно использовать uuid из стандартной библиотеки Python.

Обновим BOOKS в server/app.py :

Также не забудьте импортировать:

Рефакторинг all_books для учёта уникального идентификатора при добавлении новой книги:

Добавим новый обработчик маршрута:

Добавим вспомогательную функцию:

Клиент

Будем идти пошагово:

  1. Добавление modal и формы.
  2. Обработка нажатия кнопки Update.
  3. Подключение AJAX-запроса.
  4. Оповещение пользователя (Alert).
  5. Обработка нажатия кнопки Cancel.

1. Добавление modal и формы

Сначала добавим новый modal к темплейту, сразу после первого modal:

Добавим стейт формы в часть data раздела script :

2. Обработка нажатия кнопки Update

Добавим новый метод для обновления значений в editForm :

Затем добавим метод, обрабатывающий отправку формы:

3. Подключение AJAX-запроса

4. Оповещение пользователя (Alert)

5. Обработка нажатия кнопки Cancel

Обязательно протестируйте приложение. Убедитесь, что modal отображается при нажатии кнопки и что введённые значения заполнены правильно.

DELETE-маршрут

Сервер

Обновим обработчик маршрута:

Клиент

Добавим методы для обработки нажатия кнопки, а затем удалим книгу:

Когда пользователь нажимает кнопку удаления, вызывается метод onDeleteBook() , который запускает другой метод removeBook() . Этот метод отправляет DELETE-запрос на сервер. Когда приходит ответ, отображается Alert и запускается getBooks() .

Заключение

В этой статье были рассмотрены основы настройки CRUD-приложения с помощью Vue и Flask. Исходный код из тега v1 вы можете найти в репозитории flask-vue-crud.

А будем мы творить модальные окна. Да опять они. Но не такие простые, как описаны в первой моей (не моей) публикации.

Много уже их создано для Vue. Пользовался всякими. И видимо, когда достигаешь какого-то определенного уровня владения инструментом (в данном случае Vue), сразу хочется сделать велосипед, но конечно со своими прибамбасами, типа, чтобы круче всех и т.д. И я не стал исключением из правил.

Из всех доступных модальных компонентов, использовал в основном этот — Vuedals.
Но решил я его проапгрейдить. В принципе от основы остался только EventBus и взаимодействие событий связанных с открытием-закрытием окон. Основной компонент переписан и стал оберткой-контейнером и добавлен новый компонент — само модальное окно.
Но обо всем по порядку. И статья получится очень немаленькая, кто осилит, тот красавчик :)


В основном модальные окна во всех примерах вызываются в таком стиле:

Вроде все красиво. Но!

Какие вижу недостатки такого подхода.
Во-первых, темплейт модального окна находится внутри родительского компонента, где мы его вызываем. И контекст окна не изолирован от родителя. Мне так не всегда удобно и нужно.
Во-вторых, если одно и то-же окно используется в нескольких местах, приходится дублировать код. Что не есть гуд!
В-третьих, и что наверно является самым главным недостатком — мы можем использовать модальное окно только внутри страниц или других компонентов Vue, а вот в местах типа Vuex, Router, да и вообще в любых скриптах не можем. Мне например, надо вызвать модальное окно входа/регистрации из роутера или из стора при каком-то событии. Примеров можно привести мильён.

или полноценный, который мы импортнули извне, мне оказался больше по душе.
Больше контроля, возможностей переиспользования и вызвать такое окно можно практически отовсюду.

Выглядит в общем это так, у нас есть компонент ModalWrapper, который мы импортируем в приложение и вставляем например в корневой App-компонент. Где нибудь внизу.
Потом в любом месте вызываем метод this.$modals.open(< component: SimpleModal, title: 'Simple modal'>), куда передаем настройки нашего окна и компонент, который будем показывать и видим наше модальное окошко, которое рендерится в ModalWrapper.
Есть куча событий, возникающие при всех манипуляциях с окнами, события эти управляются при помощи EventBus-а, и их можно прослушивать и как-то реагировать.
Это вводные данные, чтобы информация легче осваивалась. В принципе, статья больше для новичков в Vue. Но надеюсь, будет пара моментов интересных и для искушенных.

Буду кидать куски кода и попутно комментировать, что к чему. Ссылка на примеры и исходники в конце есть.

Ну и пожалуй начнем с главного файла —

  1. ModalWrapper.js — общая обертка для вывода наших окон
  2. Modal.js — собственно сам компонент модального окна. Его нет в оригинальном Vuedals. Использовать напрямую его не обязательно. Он в любом случае работает под капотом. Дальше по ходу пьесы увидите этот финт ушами и станет понятно для чего я его добавил.
  3. Bus.js — EventBus для коммуникации между компонентом-оберткой (ModalWrapper), модальными окнами (Modal) и нашим приложением VueJs.

Сразу приведу код Bus.js и опишу что там происходит. Как говорил ранее, EventBus оставил так, как есть в оригинале.

Здесь мы создаем singleton-экземпляр EventBus-а, который может подписываться на события ($on) и вызывать ($emit) события. Думаю здесь объяснять особо нечего. EventBus собирает коллбеки и когда надо их вызывает. Дальше по ходу дела будет видно и понятно, как он связывает все наши компоненты.

А теперь по index.js

Здесь мы цепляем к глобальному VueJs (через prototype) экземпляр Vue под именем $modals.
В его методе created (который запустится сразу, после запуска приложения) мы подписываем наш EventBus к событиям opened (открытие окна), closed (закрытие окна) и destroyed (окон нет, убираем ModalWrapper). При возникновении этих событий EventBus будет эмитить события modals:opened, modals:closed и modals:destroyed в компонент $modals. Эти события мы можем слушать везде, где доступен самолично VueJs.
Вообще, я сначала хотел повыкидывать половину этих коммуникаций, так как некоторые совсем не обязательны, но подумав оставил. Может пригодиться для сбора какой-то статистики по модальным окнам, например. Да и начинающие, возможно что-то для себя поймут в этой, казалось бы, каше из $on, $emit — вызовов :)

Дальше this.$on…
Здесь мы включаем прослушивание событий new, close, dismiss самим компонентом $modals. При возникновении этих событий, вызываются соответствующие методы компонента $modals. Которые в свою очередь открывают (open), закрывают (close) и отменяют (dismiss) окно.
Как вы видите, у нас есть два способа закрыть окно — dismiss (отменить или по буржуйски — cancel — из той же оперы) и close (закрыть). Разница в том, что при закрытии модального окна через close, мы можем передавать данные в функцию обратного вызова onClose (рассмотрим далее), которую мы цепляем к опциям нашего нового модального окна.

И собственно методы open, close и dismiss компонента $modals. В них мы и запускаем через EventBus, события new, close и dismiss в нашем ModalWrapper. Там уже и будет происходить вся магия.

И последнее в install-функции файла index.js.

Здесь мы расширяем через Vue-миксин всем компонентам Vue метод created, в котором при запуске включаем прослушку компонентами событий modals:new, modals:close и modals:dismiss и при их вызове, через EventBus опять же запускаем соответствующие события в ModalWrapper.

Все эти адовы вызовы здесь нужны для управления нашими модальными окнами. И дают нам 4 варианта запуска событий open, close и dismiss.
Первый способ вызова нашего модального окна в приложении:

И четвертый (для этого способа нам нужно импортировать Bus.js, но это дает нам возможность вызвать окно не из компонента Vue, а из любого скрипта):

Следующий больной — Modal.js или мы не ищем легких путей

Дальше пойдет код повеселее. С недавнего времени в своих компонентах использую render-функции (как стандартные так и в jsx-формате). Использовать начал повсеместно тогда, когда понял, что они дают больше возможностей для рендеринга. С render-функциями вся мощь Javascript-а плюс крутая внутренняя VueJs кухня с vNode дают ощутимые бонусы. В момент их появления я на них как-то косо глянул, подумал, нафиг надо и продолжил рисовать компоненты в template-шаблонах. Но теперь я знаю, где собака зарыта :)

Modal — это полноценный компонент, который рендерит само модальное окно. У него куча входящих параметров:

В коде прокомментировал все параметры, чтобы было нагляднее. А код в спойлер кидаю, чтобы не получилась портянка. Итак текста немало.

Вначале импортируем иконку-крестик в виде функционального компонента, который рендерит ее в SVG-формате.

Зачем я ее так сообразил, даже не знаю.

Дальше параметр name — ну это стандартный ход.
А вот componentName здесь неспроста. Он нам нужен будет дальше, в ModalWrapper-е при рендеринге.

И вычисляемый параметр propsData:

Здесь уже сложнее.

А дело вот в чем. В оригинальном Vuedals все окна тоже вызываются с помощью тех 4-х способов, описанных выше. В каждом из них мы должны передавать компонент, который хотим показать в окне и параметры окна (все они сейчас есть во входящих параметрах Modal и плюс добавлены несколько новых). И если мы хотим запустить одно и то же окно в разных частях приложения, мы каждый раз передаем параметры окна (размеры, другие настройки). Что опять является дублированием. Да и запутаться недолго. А мы, программисты, существа крайне ленивые в основе своей. Поэтому и был создан этот компонент Modal.
Теперь мы можем создать компонент модального окна, например, вот так:

То есть, стандартный компонент. Обернутый нашим Modal (vu-modal). Этому vu-modal мы передаем нужные нам параметры. Они и будут значениями по умолчанию для этого окна.
И теперь мы вызываем это окно так:

Все нужные нам значения дефолтных настроек окна берутся автоматом из того самого компонента SimpleModal, снятых с обертки vu-modal. Мы один раз создали компонент окна с нужными нам настройками и потом используем его в любом месте не парясь о настройках. Более того, если нам надо переназначить те значения по умолчанию, мы указываем нужные нам значения при вызове этого окна:

  • props (дефолтные значения в modal.js)
  • props (в шаблоне компонента, обернутого vu-modal)
  • options (при вызове окна)

Так вот вычисляемое свойство propsData в компоненте vu-modal возвращает нам правильные входящие параметры (props), учитывая момент, является ли данный экземпляр vu-modal оберткой в каком-то компоненте (SimpleModal) или же нет.
Для этого при рендеринге окна в ModalWrapper, если компонент этого окна обернут в vu-modal мы будем передавать смердженные props-ы под именем vModal, в другом случае будем передавать обычные props-ы.
Но так как в случае, когда компонент обернут в vu-modal, при рендере props-ы будут попадать в этот компонент-родитель (SimpleModal), мы и проверяем, есть ли у компонента-родителя входящий параметр с именем vModal. Если есть, то берем эти значения, иначе стандартные props-ы.
А проверяем мы не у this.$parent.$options.propsData, а именно у this.$parent.$vnode.data.props, потому что если у компонента-родителя не прописан в props-ах параметр vModal, то при рендеринге этот vModal мы cможем увидеть только у this.$parent.$vnode.data.props. Сюда попадают все без исключения параметры, которые мы передали. А потом уже фильтруются и лишние отбрасываются.
Приведу еще раз этот кусок кода, он маленький, чтобы с мысли не сбивать)

Сейчас возможно не совсем все понятно. Информации много, и не совсем все стандартное, как во многих уроках. Пишу такого рода статью в первый раз, не совсем пока ясно, как лучше преподать. Во внутренностях Vue копаются наверняка многие, но мало кто пишет об этом. Сам долгое время искал инфу по таким моментам. Что-то находил, остальное ковырял сам. И хочется рассказывать о таких вещах.
Но более понятно станет, когда будем разбирать ModalWrapper. Там мы будем формировать и отправлять смердженные props-ы нашим окнам.

Ну и осталась render-функция нашего компонента Modal (vu-modal):

Здесь вроде ничего необычного.

Сначала вытаскиваем все наши параметры из ранее описанного вычисляемого значения propsData.
Выводим кнопку-крестик, которое вызывает событие dismiss (отмену окна), если свойство dismissable равно true.
Формируем header — если нашему vu-modal передан слот с именем header (this.$slots.header) рисуем этот слот, если передано свойство title — выводим его, иначе header вообще не показываем.
Формируем блок body с содержимым дефолтного слота (this.$slots.default).
И следом footer — если передан слот footer (this.$slots.footer).
Дальше мы определяем правильные значения для css-свойства transform: translate(x, y) нашего окна. А именно параметры X и Y в зависимости от переданных свойств нашему окну. И потом мы передаем при рендере этот transform главному div-у окна для правильного позиционирования.
Ну и рендерим все это дело, попутно вычисляя нужные класс.

И плюс вешаем на главный div.vu-modal__cmp onClick-обработчик, с event.stopPropagation(), чтобы клик по окну не всплывал выше, дабы не активировать клик по div-у (маске), которым обернуто каждое окно и которое реагирует на клик и вызывает dismiss. Иначе сработает это событие dismiss на маске и наше окно закроется.
Уффф!

Завершающий компонент — ModalWrapper

import './style.scss'
import Bus from './utils/bus'
import ModalCmp from './modal'

export default name: 'vu-modal-wrapper',
data () return modals: []
>
>,
mounted() if (typeof document !== 'undefined') document.body.addEventListener('keyup', this.handleEscapeKey)
>
>,
destroyed() if (typeof document !== 'undefined') document.body.removeEventListener('keyup', this.handleEscapeKey)
>
>,

В массиве modals мы будем хранить наши окна, которые активны в данный момент.

Ну и при монтировании и удалении нашего ModalWrapper-компонента вешаем обработчик keyup на window (если window есть), который запускает метод handleEscapeKey:

Который в свою очередь, если нажата Esc-клавиша и есть окно(а) и у текущего (запущенного последним) окна свойство escapable равно true, запускает метод dismiss, который закрывает это самое текущее окно.

Ну и пожалуй самое интересное началось. Попробую описывать происходящее прямо в коде, может так лучше будет.
При создании нашего ModalWrapper включаем прослушку событий от EventBus-а. Тех самых, которые запускаются в методах $modals, описанных раннее:

Читайте также: