modal popup with ember 1.0 rc6

2019-01-17 13:35发布

问题:

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?

回答1:

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


回答2:

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



回答3:

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


回答4:

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


回答5:

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.