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

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!

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.

Re-render view with component after destroy in Ember.js

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.

How to position the default loadingroute in emberjs

EmberJS provides a loadinroute which can be used to render a spinner etc. while the promise is being processed.
By default it processes under the {{outlet}}. I'm wondering if there is a way to position the render to someplace else?
For example in this jsbin: http://jsbin.com/ixazeb/8/edit I want to position the loading... on top of the App text.
I've tried to tap into the renderTemplate like this:
App.LoadingRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({ outlet: 'sidebar' });
}
});
and using it in my template like this: {{outlet sidebar}} but that didn't work.
You need to provide the target template name to append the loading template, using into: 'application'
App.LoadingRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('loading', {
outlet: 'loading',
into: 'application'
});
}
});
Now it works http://jsbin.com/ixazeb/14/edit

Ember.js: How to refresh parent route templates in a nested route scenario?

My page layout (application template) looks like this (simplified):
I use it for different routes (offer list + offer detail, customer list + customer detail). Lists are shown in the sub-navigation outlet.
My router's code:
App.Router.map(function () {
//...
this.resource('offers', function () {
this.resource('offer', { path: '/:offer_id' });
});
}
My Routes:
App.OffersRoute = Ember.Route.extend({
model: function () {
return App.Offer.find();
},
renderTemplate: function (controller, model) {
this.render('offer-list', {
into: 'application', outlet: 'sub-navigation', controller: 'offers' });
this.render('offer-list-title', { into: 'application', outlet: 'page-title' });
this.render('offer-list-content', { into: 'application' });
}
});
App.OfferRoute = Ember.Route.extend({
model: function (params) {
return App.Offer.find(params.offer_id);
},
renderTemplate: function () {
this.render('offer-title', { into: 'application', outlet: 'page-title' });
this.render('offer-content', { into: 'application' });
}
});
Now this works so far.
http://.../#/offers
shows the list and the title "Offer summary" and static html content. I click on one of the offers in the list, going to
http://.../#/offers/23
all okay: it still shows the list of offers in the sub-navigation area and the correct title and the content of the offer.
Now my problem:
If I return to the
http://.../#/offers
page (using a #linkTo helper on a menu), then the {{outlet}} / content area becomes empty (not the static html from before) and the title is still the title in {{page-title}} of the offer/23 route.
How can I let my app "re-render" the template as defined in the OffersRoute renderTemplate()?
P.S.: I'm using Ember.js 1.0.0-RC.3
Using the built-in Index routes and maintaining the ApplicationRoute -> OffersRoute -> OfferRoute hierarchy will solve your issue.
If you turn on the router transition logging you will see that when navigating to Offers you are actually entering the Offers.Index route:
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
This means that you can set your static Offers title and set the static Offers content in OffersIndexRoute and it will be correctly set the first time and set again if you link back to it from inside of an offer detail page. For this to work you also must preserve the ApplicationRoute -> Offers -> Offer {{outlet}} hierarchy by not directly rendering everything into the ApplicationRoute's {{outlet}}. The reason you must preserve this hierarchy is that by rendering the child (Offer template) directly into the Application template you remove the Offers template and when you try to go back to the OffersRoute its template has been removed and it shows nothing.
Index route
Use OffersIndexRoute to fill in the ApplicationRoute's {{outlet}} and the {{outlet page-title}}.
JS:
//this renders the title and the main content for Offers
App.OffersIndexRoute = Ember.Route.extend({
renderTemplate: function (controller, model) {
this.render('offer-list-title', { into: 'application', outlet: 'page-title' });
this.render();
}
});
App.OffersRoute = Ember.Route.extend({
model: function () {
return App.Offer.find();
},
renderTemplate: function (controller, model) {
this.render('offer-list', {
into: 'application', outlet: 'sub-navigation', controller: 'offers' });
// render this in OffersIndexRoute instead
//this.render('offer-list-title', { into: 'application', outlet: 'page-title' });
this.render('offer-list-content', { into: 'application' });
}
});
Handlebars:
<script type="text/x-handlebars" data-template-name="offer-list-content">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="offers/index">
Offers content
</script>
The outlet in the offers-list-content will be filled in by the OffersIndexRoute or by the Offer template, depending on what the current route is.
Maintaining {{outlet}} hierarchy
Allow the OfferRoute to render it's content template into the OffersRoute template instead of forcing it into the ApplicationRoute.
App.OfferRoute = Ember.Route.extend({
model: function (params) {
return App.Offer.find(params.offer_id);
},
renderTemplate: function () {
this.render('offer-title', { into: 'application', outlet: 'page-title' });
// preserve the hierarchy and render into the Offers {{outlet}}, which is the default
//this.render('offer-content', { into: 'application' });
this.render('offer-content');
}
});
Working JSBin example

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

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.