9
votes

How do you create a modal popup with the latest version of ember.js? Every single example I've found uses connectOutlet, which was deprecated a while ago, and the fact that I'm new to ember doesnt help.

I already have a named outlet in my application template, but how do I render my modal popup view to this outlet from a controller event? or should I use a route event?

5
Have you checked out this post? discuss.emberjs.com/t/…kiwiupover
yes, but I didnt think the solutions there were good, there is a guy accessing the DOM from the controller to show the modal. There is a really good example from a presentacion but unfortunately it uses connectOutlets and I havent been able to get it working.ryudice

5 Answers

13
votes

Adam Hawkins just published an excellent post on this topic, you can find it here: http://hawkins.io/2013/06/ember-and-bootstrap-modals/

To summarize:

  • Include {{outlet modal}} in application.hbs
  • Render into the outlet from your router by using events
  • Animation triggered by the view's didInsertElement hook and on it's close action
  • Modal's close action should target the view, which waits for animation to complete before sending close event to the router

From Adam's jsfiddle:

App.ApplicationRoute = Ember.Route.extend({
  events: {
    open: function() {
      this.render('modal', { into: 'application', outlet: 'modal' });
    },

    close: function() {
      this.render('nothing', { into: 'application', outlet: 'modal' });
    },

    save: function() {
      alert('actions work like normal!');
    }
  }
});

App.ModalView = Ember.View.extend({  
  didInsertElement: function() {
    this.$('.modal, .modal-backdrop').addClass('in');
  },

  layoutName: 'modal_layout',

  close: function() {
    var view = this;
    // use one of: transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd
    // events so the handler is only fired once in your browser
    this.$('.modal').one("transitionend", function(ev) {
      view.controller.send('close');
    });

    this.$('.modal, .modal-backdrop').removeClass('in');
  }
});
5
votes

When using Bootstrap 3.0 and final Ember 1.0, I couldn't get this code working. I rewrote it a bit (in coffeescript, layout and template handlebars are already precompiled to js with Grunt's emberTemplates)

app.coffee

App.ApplicationRoute = Ember.Route.extend({

    actions: 
        open: ->
            console.debug('open action triggered')
            @render('ContactModal', {into: 'profile', outlet: 'contactModal'})

        close: ->
            @render('nothing', {into: 'profile', outlet: 'contactModal'})

        save: ->
            alert('Send the message to person')
})

modal_view.coffee

App.ModalView = Ember.View.extend({
didInsertElement: ->
    @$('.modal').modal('show')
    view = @
    @$('.modal').on("hidden.bs.modal", (ev)->
        view.controller.send('close')
        return
    )

layout: Ember.TEMPLATES['modal_layout']

template: Ember.TEMPLATES['modal']

actions:
    close: ->
        @$('.modal').modal('hide')
        return
})

This way clicking outside the modal also closes it properly, since removing the template from outlet is done on hiding the modal.

modal.handlebars:

<div class="modal-dialog">
<div class="modal-content">
  <div class="modal-header">
    <button type="button" class="close" {{action close target="view"}}>&times;</button>
    <h3 class="modal-title">Contact</h3>
  </div>
  <div class="modal-body">
    <p>Here will go the contact form and contact template picker</p>
  </div>
  <div class="modal-footer">
    <a href="#" class="btn btn-default" {{action close target="view"}}>Close</a>
    <a href="#" class="btn btn-primary" {{action save}}>Send</a>
  </div>
</div>

modal_layout.handlebars

<div class="modal fade" role="dialog">{{yield}}<div>

I also put togheter a jsfiddle: http://jsfiddle.net/bG4F8/5/ Have fun :)

0
votes

Thank you for sharing your code Mike & Damian! Here is how I used it:

Router:

App.Router.map ()->
  @resource 'posts', ->
    @resource 'post', path:':post_id', ->
      @route 'modal'

Route:

App.PostModalRoute = Em.Route.extend
  setupController: (model, controller) ->
    @controllerFor('post.modal').set 'content', @modelFor('post')
    @render 'posts/modal', into: 'application', outlet: 'modal'
    # Note: you need outlet named 'modal' in application template

View:

App.ModalView = Ember.View.extend
  didInsertElement: ->
    @$('.modal').modal('show').on 'hidden.bs.modal', =>
      @get('controller').send 'close'

  layout: Ember.Handlebars.compile '<div class="modal hide fade">{{yield}}</div>'

Template:

App.ModalView
  .modal-header
    button type="button" class="close" data-dismiss="modal" aria-hidden="true" ×
    h3
      | Post #
      = id
  .modal-body
    ...
0
votes

Using a component, and relying on Bootstrap's own dismissal functionality to trigger a sendAction. willDestroyElement takes care of tearing things down. It's in CoffeeScript and Emblem.js, because I lifted this from my code:

application.coffee:

  ApplicationRoute = Ember.Route.extend
    actions:
      openModal: (modalName) ->
        @render modalName,
          into: "application"
          outlet: "modal"

      closeModal: ->
        @disconnectOutlet
          outlet: "modal"
          parentView: "application"

modal-dialog.coffee:

  ModalDialogComponent = Ember.Component.extend

    didInsertElement: ->
      @$(".modal").modal "show"
      @$(".modal").on "hidden.bs.modal", => @sendAction()

    willDestroyElement: ->
      @$(".modal").modal "hide"
      @$(".modal").off()

modal-dialog.embl:

.modal.fade
  .modal-dialog
    .modal-content
      .modal-header
        button.close type="button" data-dismiss="modal"
          span aria-hidden="true" &times;
          span.sr-only Close
        h4.modal-title Modal title
      .modal-body
        = yield
      .modal-footer
        button.btn.btn-default type="button" data-dismiss="modal" Close
        button.btn.btn-primary type="button" Save

modal.embl:

= modal-dialog action="closeModal"
  p Hello
0
votes

If you are looking for a more visual and simple solution to your problem, I highly recommend you check out this youtube video by Brett Valentine.

Binding to Twitter Bootstrap with Ember

I've met the developer at my local emberjs meetup, but he covers integrating bootstrap modals into projects as components.

It makes sense to integrate bootstrap elements as reusable components because you are likely going to be using them in other projects in the future.