I am following the Ember cookbook for rendering a route into a modal here: http://emberjs.com/guides/cookbook/user_interface_and_interaction/using_modal_dialogs/. This works, but I am not sure how to pass variables to my rendered view.
Specifically I want to load a 'users/filters' route into the modal, which has access to a jobTitles array. This is defined in my application route simply as this.store.find('jobTitle'). The problem is that this does not seem to be accessible from the users/filters controller or template. The users/filters route doesn't seem to be run at all because I am using the render method as follows:
App.ApplicationRoute = Ember.Route.extend({
actions: {
openModal: function(modalName) {
return this.render(modalName, {
into: 'application',
outlet: 'modal'
});
}
}
});
How can I pass this into the rendered modal? Many thanks.
One possibility would be to pass a controller to the modal rendering function:
App.ApplicationRoute = Ember.Route.extend({
actions: {
openModal: function(modalName, controller) {
return this.render(modalName, {
into: 'application',
outlet: 'modal',
controller: controller
});
}
}
});
With the above code call the openModal hook in your route's template and pass the controller name (the name, not the controller itself) of the route to it. This way you should be able to access all properties of the controller.
Related
In ember, is there a way to render a template into an outlet from a controller to get the desired effect of:
this.render('some_template', {
into: 'template_name',
outlet: 'template_outlet'
});
Or better yet, call an action in router.js from a controller?
you can put a method in the corresponding route to the controller under the actions hash (Example: posts.index controller and posts.index route) and call it using send
posts.index controller
this.send('exampleAction', record);
posts.index route
actions: {
exampleAction: function(record){
console.log(record);
}
}
Instead of outlet, you can use a component:
<script type="text/x-handlebars" id="components/x-outlet">
{{ partial template }}
</script>
Then, in your controller, you can have a template property that you can pass to the component to display your template dynamically:
App.IndexController = Ember.ArrayController.extend({
template: function(){
return 'this_one';
}.property(),
actions: {
that_one: function(){
this.set('template', 'that_one');
}
}
});
Working example here
Not completely sure what you mean by
Or better yet, call an action in router.js from a controller?
but if you are just trying to transition into a different route, you can use the transitionToRoute() method (see here)
So I have this issue where Ember will not render my view more than once, even after I have destroyed it.
The code I have, works without using components, so it is probably some issue with the actual view not being destroyed properly.
I render into an outlet in my ApplicationRoute
App.ApplicationRoute = Em.Route.extend({
actions: {
showModal: function() {
// This does not work the second time:
this.render('modal', {
into: 'application',
outlet: 'modal'
});
}
}
});
I set up an event listener for when the Bootstrap modal is hidden
App.BaseModalComponent = Em.Component.extend({
afterRenderEvent: function() {
var self = this;
this.$('.modal')
.on('hidden.bs.modal', function(){
// I am destroying the component,
// when the modal is hidden
self.destroy();
})
.modal();
}
});
The afterRenderEvent is a listener I have attached to the view's afterRender event.
See here for markup, etc.: http://emberjs.jsbin.com/wolicutiwiro/1/edit
A working example without using components: http://emberjs.jsbin.com/lodamojikaqo/1/edit
Check this JSBin It does what you want.
App.ApplicationRoute = Em.Route.extend({
actions: {
showModal: function() {
// This does not work the second time:
this.render('modal', {
into: 'application',
outlet: 'modal'
});
},
closeModal: function() {
console.log("closing modal");
return this.disconnectOutlet({
outlet: 'modal',
parentView: 'application'
});
}
}
});
I believe the main challenge is that I cannot call a closeModal action
from a button in my modal view. Bootstrap itself handles hiding the
modal, but I need to disconnect the outlet to allow the same or
another modal to render.
In order to call this action from your component, you have to send the action from the component to the controller current templates controller:
App.BaseModalComponent = Em.Component.extend({
afterRenderEvent: function() {
var self = this;
this.$('.modal')
.on('hidden.bs.modal', function(){
self.sendAction('action');
})
.modal();
},
});
And when you use your component, make sure to assign the action name:
<script type="text/x-handlebars" data-template-name="modal">
{{#base-modal action='closeModal'}}
<p>One fine body…</p>
{{/base-modal}}
</script>
The action will bubble from the controller to the route. This solution allows you to use bootstrap exactly as is, but I find that the solution Code Jack suggested to be much more Ember.
I have routes such that,
Router.map(function() {
this.resource('board', { path: '/boards/:board_id' }, function() {
this.route('calendar');
this.route('attachments');
this.resource('card', { path: '/cards/:card_id' });
});
});
and the board template has two outlets, one is the default, and the other is named outlet.
/templates/board.hbs
<div id="master-content">
{{outlet}}
</div>
<div id="detail-content">
{{outlet 'detail'}}
</div>
/routes/card.js
App.Card = Ember.Route.extend({
renderTemplate: function(controller, model) {
this.render('card', { outlet: 'detail', controller: controller, into: 'board' });
}
});
When I transition to board.index or board.calendar or board.attachments, their templates will be displayed in the default outlet and I want to display the card template into the outlet named 'detail'.
Here I have a question. Based on how Ember works in general, when I move to card route, the card template will be into detail outlet, but the other default outlet will become empty. I'd like to keep the default outlet as it was when I move to the card route.
My first approach is to have a controller that stores the information of what was in the default outlet and render them again whenever I move to card route.
Any best practices about this situations?
I personally have never used Query Parameters in Ember (They're still in experimental and available in canary builds) but I believe they are a good fit for you.
http://emberjs.com/guides/routing/query-params/
You can use the full transition in the route (here) to have the templates rendered according to the values from query parameters.
App.CardRoute = Ember.Route.extend({
model: function(params) {
this.set('calendar', params.calendar); //assuming calendar it's the name of your query param
this.set('attachements', params.attachements); //assuming calendar it's the name of your query param
},
renderTemplate: function(controller, model) {
this.render('card', { outlet: 'detail', controller: controller, into: 'board' });
if (this.get('calendar')) {
this.render('calendar', { controller: 'calendar', into: 'board' });
} else if (this.get('attachements')) {
this.render('attachements', { controller: 'attachements', into: 'board' });
}
},
actions: {
queryParamsDidChange: function() {
// This is how we opt-in to
// a full-on transition that'll
// refire the `model` hook and
// give us a chance to reload data
// from the server.
this.refresh();
}
}
});
Your other option would be to have the card resource as a subroute of both calendar and attachments routes.
I hope this helps you!
So I am trying to figure out how best to put modals on a route, such that you can navigate to them via the url.
application.hbs
{{outlet}}
{{outlet modal}}
There is a discussion here and emberjs cookbook provides another example but nothing covers how you can have modals on a specific route.
The closest thing I have seen is this Stack Overflow question but it suffers from two problems:
When the modal route is visited, the view in the main outlet gets destroyed. So in your UI, things underneath the modal get wiped out.
history.back() is that you essentially revisit that route causing that view to be redrawn and feels very hackish.
This is where I feel a solution would exist but not sure what exactly:
App.MyModalRoute = Ember.Route.extend({
renderTemplate: function(controller, model) {
/**
* When my modal route is visited, render it in the outlet
* called 'modal' but somehow also persist the default outlet.
**/
this.render({ outlet: 'modal' });
}
});
Here is how we handle it:
In the route you use a render piece similar to what you described:
App.UserDeleteRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({
into: 'application',
outlet: 'modal'
});
},
actions: {
closeModel: function() {
this.transitionTo('users.index');
}
},
deactivate: function() {
this.render('empty', {
into: 'application',
outlet: 'modal'
});
}
}
Clearing out outlet on the "deactivate" hook is an important step.
For the main template all you need is:
{{outlet}}
{{outlet "modal"}}
This does not override the default outlet.
One thing you may want to do for all your modals is implement a layout:
App.UserDeleteView = Ember.View.extend({
layoutName: 'settings/modals/layout',
templateName: 'settings/modals/user_delete'
});
Another gotcha is that there needs to be a parent route rendered into the main outlet. If whatever is in that outlet is not the parent of your modal, then the act of moving away from that route will remove it from the outlet.
working JS bin
You've got several options here:
make modal route child of route you want to persist - than either render modal nested in application template, like in example you mentioned, or put it inside its own template id="myModal"
add modal outlet inside persisted route's outlet and render it in renderTemplate method
renderTemplate: function(controller, model) {
this.render(); //render default template
this.render('myModalTemplate', { //render modal to its own outlet
outlet: 'modal',
into: 'myModal', //parent view name. Probably same as route name
controller : controller
});
}
Besides, you can render template with modal in named outlet any moment(on action f.e.), by just calling render method with proper arguments
By default Ember inserts the view of a child resource into an {{outlet}} defined by a view of a parent resource. How do I override that ? i.e. insert the child view in the {{outlet}} defined by the application view. Why is this the default?
Usecase: There is a users resource, with a new route inside it. I want the new to show in the applications {{outlet}} rather than the parent resource's {{outlet}}.
App.Router.map(function(){
this.resource('users', function(){
this.route('new');
});
});
For each route we have a renderTemplate method that we can overload. This gives us full control over the rendering of the views.
For example, we can specify into which {{outlet}} the view will render with into:
(I assume this is your use case, but I'm a little absent-minded today.)
var UsersRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('users', {
// Render the UsersView into the outlet found in application.hbs
into: 'application'
});
}
});
We can also specify the name out of outlet to render into using the outlet property:
var UsersRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('users', {
// Render the UsersView into the outlet named "sidebar"
outlet: 'sidebar'
});
}
});
And of course we can use a combination of both to specify both the outlet's name, as well as where that outlet is found using the into property.