Добрый день. В этой статье я разберу способ, который позволяет при смене адреса показывать модальное окно для наших нужд. Я знаю, что есть собственное решение для компонента «modal» на официальном сайте. Вдобавок к этому на Хабре есть много других статей, посвященных теме модальных окон во VueJs (например, вот эта).

Однако, на мой взгляд, каждый из них имеет свои минусы, например:

  • в случае изложенном на офф сайте компоненты нужно создавать отдельную переменную и привязываться к ней;
  • во второй статье рассматриваются решения сторонних разработчиков. В этом случае нужно потратить время и разобраться как использовать эти компоненты + лишние, на мой взгляд, зависимости в проект. А я придерживаюсь позиции: чем проще, тем лучше.

Опять же, я всего знать не могу, поэтому, если если у вас есть что сказать или отметить, то я открыт к конструктивной критике. Далее мы будем использовать исключительно стандартный функционал инструментов, которые используем (Vue, VueRouter, Bootstrap Modal). Итак, ближе к делу…

Найдутся люди, которые скажут: «лучше подключить npm-зависимости для modal, чем тянуть весь bootstrap + jquery.». Товарищи, вам никто не мешает все это дело адаптировать под ваши нужды и инструменты. Приведенные инструменты лишь пример реализации.

Сразу рабочий вариант.

HTML
<div id="app">
  <nav>
    <router-link :to="{ name: 'modal' }" exact>Open Modal</router-link>
  </nav>
  
  <router-view></router-view>
</div>


javascript
Vue.use(VueRouter)

const Modal = {
	template: `<div class="modal fade" tabindex="-1" role="dialog" ref="modal">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">?</span>
        </button>
      </div>
      <div class="modal-body">
        <p>Modal body text goes here.</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>
mounted: function(){
	console.log('mounted')

	var context = this;

	$(this.$refs.modal).modal('show')
  
  $(this.$refs.modal).on('hidden.bs.modal', function(){
  	context.$router.go(-1);
  })
}
}


const routes = [
  { path: '/modal', name: 'modal', component: Modal },
]

const router = new VueRouter({
	routes,
})

// New VueJS instance
var app = new Vue({
	// CSS selector of the root DOM element
  el: '#app',
  // Inject the router into the app
  router,
})


Собственно, здесь нет ничего сложного.

  1. При переходе по нашей ссылке, монтируется наш компонент Modal.
  2. При его монтировании мы открываем наше модальное окно и через this.$refs отслеживаем его закрытие.
  3. При его закрытии мы программно возвращаем человека на шаг назад, чтобы наш компонент отмонтировался.

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

  • легкая кастомизация;
  • не нужно подгружать сторонние зависимости. Лично для меня чем проще, тем лучше;

Комментарии (10)


  1. HeTpe3B
    05.08.2019 21:28

    Рендерить jQuery с помощью Vue?)


  1. Tantacula
    05.08.2019 21:54

    Я правильно понял, что вы не захотели подключать сторонние библиотеки, но подключили бутстрап и jquery, чтобы написать свой код?


  1. juDge
    04.08.2019 22:42
    +1

    Знатное извращение… если надо логику степ бай степ… то зачем нужно модальное окно вообще?… можно просто отслеживать историю хождений и тп


  1. krendel122
    04.08.2019 23:45
    +1

    — var в 2019 году
    — jquery + $refs
    — модалка заменит содержимое текущей страницы в router-view, а не просто отрендерится поверх
    — переиспользовать невозможно, где хотя бы слоты для контента?
    — перезагрузка страницы на урле модалки при закрытии куда юзера кинет?


  1. moxy Автор
    04.08.2019 23:57
    -4

    • var в 2019 году — да, представляете какой я старый?!) На самом деле Вы действительно считаете это критичным?;
    • jquery + $refs — в статье отмечено, что это лишь пример реализации. В ваших проектах, наверняка, используются другие kit-ы;
    • модалка заменит содержимое текущей страницы в router-view, а не просто отрендерится поверх — Вы пример-то смотрели?;
    • переиспользовать невозможно, где хотя бы слоты для контента — в моей задумке ( каждое отдельное окно — это отдельный уникальный компонент со своей логикой, кодом и структурой. Мне видится, что «переиспользовать» здесь ни к месту;
    • перезагрузка страницы на урле модалки при закрытии куда юзера кинет? — Вы пример смотрели?


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


    1. SteveLeo
      05.08.2019 09:46

      По пункту 3. Я правильно понимаю, Вы предлагаете писать отдельный роут для каждого модального окна и добавлять на каждую страницу где используется модальное окно <router-view>? Без <router-view> всегда будет происходить перересовка основного контента страницы jsfiddle.net/leos0331/4L8wvh3y.


  1. userNCh
    05.08.2019 09:46

    Вы не по назначению используйте router-link. Оно, конечно, у вас работать будет, но другие разработчики после вас увидев в коде router-link сразу не догадаются, что это просто кнопка для раскрытия модалки. router-link всё-таки это про переходы на другие страницы сайта. Для раскрытия модалки (с точки зрения семантики) лучше применять button.


  1. CyberAP
    05.08.2019 13:20

    В чём проблема с тем чтобы привязать модальное окно к модулю Vuex и использовать middleware роутера?
    Вешать логику роутера на модальное окно — это грубое наршение SRP. Появилась ненужная связь модального окна и роутера, такой код будет невозможно поддерживать.
    Использование Vuex избавит вас от глобальных переменных и позволит более гибко контролировать поведение модального окна.
    Вам правильно подсказали что такой подход будет перезагружать страницу. У вас на начальной странице ничего нет. Попробуйте добавить туда что-нибудь и увидите как при каждом закрытии окна весь контент рендрится заново. (пример: https://jsfiddle.net/uyqmL7ro/)


  1. parmactep
    06.08.2019 07:07

    Возвращать на шаг назад в истории — так себе решение. А если я по внешней ссылке перешёл?