ryudice
ryudice

Reputation: 37376

modal popup with ember 1.0 rc6

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?

Upvotes: 9

Views: 5722

Answers (5)

Branden Silva
Branden Silva

Reputation: 1406

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.

Upvotes: 0

neverfox
neverfox

Reputation: 7030

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" ×
          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

Upvotes: 0

tomaszbak
tomaszbak

Reputation: 8287

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
    ...

Upvotes: 0

Damian Walczak
Damian Walczak

Reputation: 1334

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 :)

Upvotes: 5

Mike Grassotti
Mike Grassotti

Reputation: 19050

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');
  }
});

Upvotes: 14

Related Questions