-
Notifications
You must be signed in to change notification settings - Fork 13
Usage
Previous: Installation | Next: Customization
Core functionality is added to your application using the modal
controller. The modal
controller is injected into all your application's controllers giving you easy access to new public methods throughout your application.
- Rendering a simple modal
- Rendering an advanced modal
- Rendering a modal programatically
- Using multiple outlets
- Removing a modal
First, make sure you've added the modal
outlet to your template(s):
{{!--app/templates/application.hbs--}}
{{outlet}}
{{outlet 'modal' name='modal'}}
The simplest way to render a modal is by calling showModal()
from any controller. showModal
accepts a single argument, which may be a string or an object depending on your use case.
To render a modal in the current context (the route's controller) pass a template name as a string. By default, the template will be rendered inside the modal
view. For example, if we were to call showModal
from inside an action:
// app/controllers/some-controller.js
import Em from 'ember';
export default Em.ObjectController.extend({
actions: {
confirm: function() {
this.showModal('confirm-choice');
}
}
});
In the above example, app/templates/confirm-choice
will be rendered inside the modal
view. This is essentially an extension of your current route's template - just seperated visually - and you can bind properties in the confirm-choice
template as if it was part of the route's template.
If you want control over the context in which a modal is rendered, you can pass any combination of template
, controller
, model
, and view
as an object to the showModal()
method. For example:
// app/controllers/some-controller.js
import Em from 'ember';
export default Em.ObjectController.extend({
currentChoice: Em.Object.create({
name: 'Mike',
id: 4
}), // Some object
actions: {
confirm: function() {
this.showModal({
template: 'confirm-choice', // String
controller: 'form', // String
model: this.get('currentChoice') // Object
});
}
}
});
Inside your chosen template, the model you pass is accessible as follows:
{{!--app/templates/confirm-choice.hbs--}}
{{#with modal.model}}
{{name}} // Mike
{{id}} // 4
{{/with}}
Please note, model
is not set as the model on the specified controller - it's set as the model on the modal controller. This gives you more flexibility over how you handle data without overriding the current route's controller's existing content.
showModal
accepts an options as a second argument called renderingOptions
. renderingOptions
can have any combination of the following properties:
// app/controllers/some-controller.js
import Em from 'ember';
export default Em.ObjectController.extend({
actions: {
confirm: function() {
this.showModal({
template: 'confirm-choice'
}, {
outlet: 'loading-screen', // Defaults to 'modal'
parentView: 'index' // Defaults to 'application'
});
}
}
});
Whether you specify outlet
, parentView
, or both, the outlet must be found in the specified or default view's template. For example, the above rendering options correspond to the following template:
{{!--app/templates/index.hbs--}}
{{outlet 'loading-screen'}}
Warning: If you use the same named outlet in different views you will see unexpected behaviour.
As you can imagine, this allows you to use ember-modals
for much more than just dialog-like UI. This addon can manage outlets throughout your application.
Warning: If the templateName
of the view specified using parentView
has a different name from the view (e.g. your index
view uses a template called home
) you will not be able to use the built in rendering methods.
Should you wish to have more control over the rendering of a modal, you can set the following properties from any controller.
Warning: Manipulating the modal using get
and set
method is not recommended and can lead to unexpected behaviour when using multiple outlets. Where possible use the showModal
method.
modal.controller // String
modal.model // Object
modal.template // String
modal.view // String
modal.defaultOutlet // String
modal.defaultParentView // String
modal.defaultView // String
modal.transitionDuration // Number
A single property can be set as follows:
// app/controllers/some-controller.js
import Em from 'ember';
export default Em.ObjectController.extend({
doSomeSetup: function() {
this.set('modal.controllerName', 'form');
}
});
Multiple properties can be set as follows:
// app/controllers/some-controller.js
import Em from 'ember';
export default Em.ObjectController.extend({
doSomeSetup: function() {
this.get('modal').setProperties({
controller, 'form',
defaultOutlet: 'backup-modal',
model: this.get('content')
});
}
});
Once you have setup your modal as desired you must call this.get('modal').show()
to render it with the correct properties. For example:
// app/controllers/some-controller.js
import Em from 'ember';
export default Em.ObjectController.extend({
doSomeSetup: function() {
var modal = this.get('modal');
// Set the properties...
modal.setProperties({
template: 'confirm-choice'
controller, 'form',
defaultOutlet: 'backup-modal',
model: this.get('content')
});
// ... Then render the modal
modal.show();
}
});
In the above example, app/templates/confirm-choice
will be rendered inside the modal
view (the default) with the form
controller. The template will have access to model
using {{modal.model.someProperty}}
and the whole thing will be inserted into the backup-modal
outlet in the application
template.
show()
accepts an optional argument - the renderingOptions
object covered in rendering an advanced modal. For example:
// app/controllers/some-controller.js
import Em from 'ember';
export default Em.ObjectController.extend({
doSomeSetup: function() {
var modal = this.get('modal');
// Set the properties...
modal.setProperties({
template: 'confirm-choice'
controller, 'form'
});
// ... Then render the modal
modal.show({
outlet: 'form-modal',
parentView: 'form'
});
}
});
See customization for a better look at customizing the modal
controller's properties (and mistakes to avoid).
You can render modals and other views into different outlets in different parent views, as previously discussed. You can even show two modals at the same time. An example might be rendering a modal over a loading screen, both of which are rendered using ember-modals
.
In order to effectively manage multiple outlets you must do three things:
- Use unique outlet names across all views
- Pass the outlet name when you call
hide()
if it's different from the modal controller'sdefaultOutletName
- Add the
name
option to all outlets (see below)
To ensure everything works as intended with multiple outlets, add name
to each outlet with the exact name of the outlet:
{{!--app/templates/application.hbs--}}
{{outlet 'modal' name='modal'}}
{{outlet 'form-modal' name='form-modal'}}
Warning: ember-modals
currently only works with outlets where the view and template have the same name.
There are two ways to remove a modal, depending on your situation:
To close the modal from any controller call this.get('modal').hide()
.
This is the most common way of closing a modal. For example, you will handle the user's response from some modal UI, save the response in the controller, and then close the modal only after a successful save. For example:
// app/controllers/some-form.js
import Em from 'ember';
export default Em.ObjectController.extend({
save: function() {
var _this = this;
// Do required task before closing modal
_this.get('content').save().then(function() {
// Then close modal
_this.get('modal').hide();
}, function(xhr) {
// Else, keep modal open and show user the error
});
}
});
Another use case might be using a modal to offer the user a choice between two different routes for navigation. In this situation, you might want to close the modal when the new route has begun loading and not before that point. So, in the route being transitioned to, you can do:
// app/route/some-new-route.js
import Em from 'ember';
export default Em.Route.extend({
// Close the modal that was opened in a previous route.
setupController: function(controller, model) {
this._super(controller, model); // Default route functionality
controller.get('modal').hide();
}
});
hide()
accepts an optional argument. This should be a string referencing the name of the outlet to remove the modal from. You will only need to use this when the modal is in an outlet that is different from the modal controller's defaultOutlet
property.
// app/route/some-new-route.js
import Em from 'ember';
export default Em.Route.extend({
// Close the modal that was opened in a previous route.
setupController: function(controller, model) {
this._super(controller, model); // Default route functionality
controller.get('modal').hide('loading-screen'); // Name of outlet
}
});
You may close a modal from inside any view or template by sending the closeModal
action. The close modalAction
just proxies the method of closing from the controller.
This is a very common way of closing a modal from the modal layout or template. If you have a close button in your template you can use this action. For example:
{{!--app/templates/some-form.hbs--}}
<button {{action 'closeModal'}}>Close</button>
As previously mentioned, you can pass the outlet name to the closeModal
action. This is only necessary if the modal is in a different outlet than the one named by the modal controller's defaultOutlet
property.
In any view extended from ember-modals/views/modal
, you will have access to the outlet
property for this very occasion:
{{!--app/views/some-modal-content.hbs--}}
<button {{action 'closeModal' view.outlet}}>Close</button>
Simple!
If you want to do some other handling as well as closing the modal then you should probably write a custom action that closes the modal from your controller. For example:
{{!--app/templates/some-form.hbs--}}
<button {{action 'continue'}}>Continue</button>
<button {{action 'closeModal'}}>Close</button>
And then in your controller:
// app/controllers/some-form.js
import Em from 'ember';
export default Em.ObjectController.extend({
actions: {
continue: {
this.doTheThing(); // Do whatever it is you need to do
this.get('modal').hide(); // Then close the mdoal
}
}
});
You may find it necessary to use promises and place the call to close the modal in the resolve callback.
Alternatively, in a view's method you can use Ember's send()
method or just do this.get('controller.modal').hide()
.
Previous: Installation | Next: Customization