Ember.js: Inserting child resource's view into the main application's outlet - ember.js

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.

Related

Passing variables to modal in ember

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.

ember.js - render a template in router.js equivalent for controllers

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)

how to keep the other outlet when transitioning to another route into named-outlet

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!

Emberjs Modals on a route

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

How to debug a missing view

From my router, I'm rendering a view:
App.MonthSummaryRoute = Ember.Route.extend({
events: {
selectTab: function(name) {
this.render(name, { into: 'month/summary', outlet: 'tab' });
}
}
});
As an example, name is "summaryCompany". If I add a
<script type="text/x-handlebars" data-template-name="summaryCompany">
<h2>Test template</h2>
</script>
this template displays. But I tried to add a view to handle the events:
App.SummaryCompanyView = Ember.View.extend({
didInsertElement: function() {
console.log('here');
}
});
and I'm not getting anything. What am I missing?
Could you provide your entire code selection, or a JSBin / JSFiddle?
Possible approaches:
What's in your month/summary template / route / view?
Maybe you can't call render from an event. What happens when instead of doing the render from inside selectTab you do it from the route's renderTemplate hook?
renderTemplate: function() { this.render("summaryCompanyView", { into: 'month/summary', outlet: 'tab' }); }
You can try seeing if the view is inserted at all: in web inspector, find the ember-id of the div corresponding to view (somethign like <div id="ember310" ...>, then access the actual view object via Ember.Views.views.ember310 (or whatever id). You can check the view's class and see if it's App.SummaryCompanyView or a generic Ember.View
Lastly, what happens if you remove the inlined-template and specify the template on the View object via templateName?