Reputation: 37376
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
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
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
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
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"}}>×</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
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:
{{outlet modal}}
in application.hbsdidInsertElement
hook and on it's close
actionclose
action should target the view, which waits for animation to complete before sending close
event to the routerFrom 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