Если вы любите Bootstrap, активно используете его в работе и решили с головой окунуться в мир ember.js, то вас ожидает увлекательнейшее путешествие по закоулкам Stack Overflow в поисках ответов на десятки вопросов. Мой путеводитель по галактике по Ember.js способен дать ответ на некоторые из них, например:

Как использовать модальные окна Bootstrap в Ember.js?

Прежде чем перейти к ответу, необходимо эффектное вступление для моего путеводителя в целом. Недолго думая вспомнились два коротких слова, которые непременно поддержат тех, кто хочет покорить вселенную ember.js

«DON'T PANIC!»

Хорошее, начало. Поехали!

Давайте прикинем, что необходимо учесть при использовании модальных окон:

  1. простая интеграция в любые части приложения
  2. передача различных моделей в модальные окна, в зависимости от ситуации
  3. регулировка размеров и стилей модальных окон

Модальное окно будем загружать через вспомогательный {{outlet 'modal'}}. К примеру мы хотим сделать редактирование песен на странице /songs. Темплейт для /songs будет выглядеть так:

// templates/songs
/*Ваш код*/

{{outlet 'modal'}} // место для отображения модального окна

Теперь нам необходимо создать компонент modal-window.hbs и вынести в него все элементы шапки и футера модального окна.

ember generate component modal-window

В нем разместим следующий код:

/templates/components/modal-window.hbs
<div class="modal fade">
  <div class="modal-dialog {{size}}">    // {{size}} - переменная для динамического изменения модального окна
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
        <h4 class="modal-title">{{title}}</h4>   //название модели
      </div>
      <div class="modal-body">
         {{yield}}                                       // сюда будем грузить уникальный контент модального окна
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button>
        <button type="button" class="btn btn-primary" {{action 'save'}}>Сохранить изменения</button>
      </div>
    </div><!-- /.modal-content -->
  </div><!-- /.modal-dialog -->
</div><!-- /.modal -->

Обратите внимание, мы добавили переменную {{size}}. В нее будем передавать класс для регулирования размера модального окна (modal-sm, modal-lg). По аналогии можно добавлять классы к любому другому блоку.

{{yield}} — это то место, где будет отображаться основная часть модального окна. К ней вернемся чуть позже. Если вы не знаете, как использовать компоненты, ознакомиться с оф.гайдом можно тут.

Неплохо было бы сразу добавить действия для кнопок «сохранить» и «закрыть», которые расположены в футере модального окна. Для этого добавим в класс нашего компонента components/modal-window.js следующий код:

//components/modal-window.js
  actions: {
    save: function() {                                        
      this.$('.modal').modal('hide');                   
      this.sendAction('save');                          
    }
  },
  show: function() {
    this.$('.modal').modal().on('hidden.bs.modal', function() {
      this.sendAction('close');
    }.bind(this));
  }.on('didInsertElement')                                //вызываем функцию после загрузки компонента

Теперь самое интересное.

Создадим новый темплейт, который будет отображаться в {{yield}} созданного выше компонента и он же будет загружаться в {{outlet 'modal'}}

ember generate template modals/song

Вставим туда следующий код

//templates/modals/song.hbs
{{#modal-window title="Добавить песню" size="modal-sm" save='save' close='removeModal'}}
                    <form {{action 'save' on='submit'}}>
                        {{input type="text" value=name class="form-control"}}
                        {{input type="text" class="form-control" value=fromServ}}
                    </form>
{{/modal-window}}

Мы передали параметры компоненту (title, size, save, close). Теперь создадим контроллер для этого темплейта:

ember generate controller modals/song

Добавим туда экшен для кнопки «save»:

// controlers/modals/song

  actions: {
    save: function() {
      // действие
    }
  }

Заключительный этап — нам нужно указать какой именно темплейт с какими параметрами грузить в {{outlet 'modal'}}.

Для этого достаточно передать параметры через кнопку вызова модального окна:

// templates/songs - темплейт для вывода списка песен

/*Ваш код*/

<button {{
   action 'showModal' //этот экшен создадим далее
   'modals/song' //передаем нужный темплейт 
   model //передаем нужную модель
}}>
   Редактировать
</button>

{{outlet 'modal'}} // место для отображения модального окна

Затем вешаем обработчик для 'showModal' прямо в route темплейта, в котором находится {{outlet 'modal'}}, в нашем случае это routes/songs:

//routes/songs
	actions: {
           showModal: function(name, model) {
	      this.render(name, {
	        into: 'songs',
	        outlet: 'modal',
	        model: model
	      });
	    },
	    removeModal: function() {
	      this.disconnectOutlet({
	        outlet: 'modal',
	        parentView: 'songs'
	      });
	    },
  	}

Еще раз о главном.

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

  1. action 'showModal' — который описываем в рауте того темплейта, в котором хотим отобразить модальное окно
  2. 'modals/song' — темплейт, который будет загружен вместе с компонентом модального окна
  3. model — модель для данного модального окна

Затем вызывается метод showModal, который помещает темплейт модального окна с нужной моделью в {{outlet: 'modal'}}
В темплейте модального окна подгружается компонент модального окна, а в месте с ним и функция show, которая отобразит модальное окно на экране.

Готово! Спасибо за внимание.

P.S.: Спасибо EmberGuru за идею.

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


  1. DSL88
    18.11.2015 17:37

    вместо {{size}} лучше использовать classNameBindings компонента для того, чтобы повесить на весь div модального компонента


    1. DSL88
      18.11.2015 17:40

      Также вместо this.$('.modal') лучше использовать this.element из-за той же причины


      1. DSL88
        18.11.2015 18:03

        да, простите, this.element можно заменить даже на this.$()


  1. DSL88
    18.11.2015 18:18

    Кстати, стоило бы добавить disable кнопки save при прохождении валидации модели =)


    1. Rathil
      19.11.2015 01:37

      А я гляжу Вам не нудно жить :)


      1. DSL88
        19.11.2015 09:05

        У меня много проектов на Ember.js. Я уже давно с ним работаю)


  1. arvitaly
    19.11.2015 05:00

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