Ember Route transition, prevent templates destroy. - ember.js

This is what i have in my app.js. Index route with flights & cars as nested routes.
this.resource('index',{ path: '/' }, function(){
this.route('flights');
this.route('cars');
});
application.hbs
<div class="main">
{{outlet}}
</div>
index.hbs
..some html here...
{{outlet}}
..some html...
cars.hbs
{{#some-component1}}
{{/some-component1}}
flights.hbs
{{#some-component2}}
{{/some-component2}}
Cars and flights are kind off tabs(links), when clicked i use link-to to load the corresponding route
domain/app/#/flights
loads flights hbs in index.hbs outlet which in turn is loaded in application.hbs outlet.
When user clicks cars url changes to
domain/app/#/cars
and it loads cars.hbs in index.hbs outlet.
But when this transition happens the flights rendered template is destroyed along with all the components it had and corresponding component class objects and cars template and all its components are rendered. Which is all fine.
Now when user moves back(click on flights link) to flights all the flights components/templates are re created and rendered.
The content of these tabs are expensive to create every time when user moves between tabs(kind off).
Question: Should it be implemented just as a tab panel and not change the url(not use link-to, just actions) when user clicks on these links and showing/hiding content?
OR
should it be done the URL way? If yes, is there a way to make ember not destroy the previously rendered template in the outlet?
Thanks

At this time you can not keep your views/components rendered when route change. You can only render it beforehand and then switch between using ember query params. Take a look at
http://emberjs.com/guides/routing/query-params/ and https://github.com/instructure/ic-tabs

Related

Return Output of some function or event to some new page or template in Meteor with Iron:router

I am working on some project, i want to return filtered data to some new page, what i am facing right now is, that output is shown on same template, so lot of extra stuff occurs which i do not want. I want to redirect it using router, but this filter function is a part of images_thumbnail.
Client > main.html
<template name="images_thumbnail">
//lot of stuff + images
{{#each images}} //show all images from public folder
<img src=""some_image.jpg" alt="img_title" />
<caption>
<a href="#" class="js-set-genre-filter">
{{img_genre}} //image_genre associated with this image
</a>
</caption>
{{/each}}
</template>
When I click image genre (say Thriller, Comedy, etc.), image filter is set
Client > main.js
Router.route('/images', function(){
this.render('navigation_bar',{
to: 'header'
});
this.render('owl_carousel', {
to: 'carousel'
});
//images_thumbnail template along with navbar, carousel
this.render('images_thumbnail', {
to: 'body'
});
});
Tempalte.images_thumbanil.events({
'click .js-set-genre-filter':function(event){
Session.set("genreFilter", this.img_genre);
console.log(this.img_genre); //gives Comedy/Thriller
}
})
Template.images_thumbnail.helpers({
images:function(){
if(Session.get("genreFilter"))
return Images.find({img_genre: Session.get("genreFilter")};
//This is the output, but i want to redirect it to some
//new template, instead of images_thumbnail template
}
})
When no filter is set
Output when no Filter is set, Navigation bar, Carousel, rest stuff is visible
Filter output, when genreFilter is set
Navigation bar, button, Headings are shown which are not needed for this content
I don't think I understand fully what you want,
if you want to redirect your route to another at the point of your code,
simply use Router.go('/newPath')
In addition,
Iron router was great package still,
but Meteor Developer Group(MDG) and a lot of people changed to use FlowRouter
because of maintanence and to focus on routing itself. (Iron Router controls pub/sub and lifecycle too much.)
So consider to FlowRouter too-

How do you create nested dynamic routes with ember.js where child route replaces parent's template?

On Ember 3.15 (Octane)
I'm trying to create the following routes in my application
/cars
/cars/:car_id
/cars/:car_id/:model_id
The first two are fine and I've been able to create them using ember-cli
ember g route cars
ember g route cars/car
The problem with the third one is that I need the model template to be shown under the cars template, ie. replace the car template. So if I put an {{outlet}} on the car template, the model template is rendered in it fine but obviously with that {{outlet}} nothing happens when I navigate to /cars/5/2
My router looks like this
Router.map(function() {
this.route('cars', function(){
this.route('car', { path: ':car_id'}, function() {
this.route('model', { path: ':model_id' });
});
});
});
I've tried using an index route ember g route cars/car/index but the problem with this that the params are not available in the index route and can only be accessed on the car route.
As plan b I've created the car and model routes as top level routes (based on this answer), but I'd like to achieve the above because of a few reasons
it seems more logical ie, structuring the app based on the nesting of the routes
I have many nested routes and creating all of them as first level routes will become hard to maintain
ember doesn't apply the active class correctly with this configuration. For example if I have a navbar with Cars as an link, I'd want it to have the active styling on all three pages. But this doesn't work anymore because the second route will be called something like car and the third one something like model.
there are some issues with the urls that are created by default using <LinkTo />.
So if I have something like this in my car template
<ul>
{{#each #model.models as |model|}}
<li><LinkTo #route="model" #model={{model}}> {{model.title}} </LinkTo></li>
{{/each}}
</ul>
The actual link works properly in that it takes me to the correct model page, however, the url is shown as /cars/undefined/undefined. (Although this is fixable by passing #models={{array #model.id model.id}}, it seems like a workaround at this point)
The solution is indeed to use the index route.
So have cars.car.index and cars.car.model. However you should use the cars.car route to load the common car model. Then you can access it in the cars.car.index route with this.modelFor('cars.car').
Then you can access the params.car_id in the cars.car route, use it to load the car, and then access this car from the cars.car.index and cars.car.model routes with modelFor.

Re-render template when changing route

I have an application with two outlets (in the application.hbs), where-as the second renders a grid of videos that is displayed no matter on which route you are on, but the videos that are displayed can change. The important bits of the model"
App.Video = DS.Model.extend({
...
frontpage: DS.attr('boolean'),
...
});
So when I am on most routes I want to display the videos with frontpage == true and on one route I want to display all the videos, no matter what frontpage says.
I seem to have gotten it half working.
The important routes look like this:
App.Router.map(function () {
this.resource('videos', { path: 'videos/' }, function () {
this.route('video', { path: ':video_id' });
});
...
And my videos.index template (which I render in the second, named outlet) looks like this:
{{#each video in controller}}
{{log isFrontPage}}
<!--if we only want to display the frontpage-videos in the grid -->
{{#if isFrontPage}}
<!--show only videos with frontpage = true -->
{{#if video.frontpage}}
{{partial 'video-thumbnails'}}
{{/if}}
{{else}}
<!--show all videos, which will be used for the /videos site-->
{{partial 'video-thumbnails'}}
{{/if}}
{{/each}}
The isFrontPage is a property in the ApplicationController, that gets set to true/false in the right routes. When I initially load the index route it all works fine and just loads the videos with frontpage==true, when I then switch to the /videos route it all works fine as well and it loads all the videos. But then, no matter what route I go on, it just stays the same. And interestingly enough, even though the displayed videos change when going on the /videos route the log command doesn't print anything anymore.
Also, I am using the renderTemplate function to get the functionality, something like that in the VideosIndexRoute:
this.render('videos.index', {
outlet: 'videogrid',
into: 'videos.index',
controller: controller
});
And in the ApplicationRoute I have the same, just the into and the controller are for the application route.
So anyone knows how I can make ember rerender that template on the router-level? Or is there anything that I am missing and there is another/better way to do this? Any help is really appreciated.
Ok, I found a solution. The problem was, that ember loads the ApplicationRoute only one single time, so the renderTemplate method was never called again. I simply created a Route-object that all the other routes extend, so the right renderTemplate gets called for the right routes.

Persisting object controller details

I have the following template setup displaying a list of items, and wrapping each list in its own controller:
{{#each controller itemController="contact"}}
<div class="contact-entry">
<div class="title" {{action toggle on="click"}}>{{title}}</div>
{{#if showDetails}}
<div class="details">
<div>{{safe details}}</div>
</div>
{{/if}}
</div>
{{/each}}
The main controller is an ArrayController called ContentsController, and each item is wrapped in an ObjectController called ContentController. The content of the ContentsController is set explicitly in the route (I've simplified the data):
App.ContactsRoute = Em.Route.extend({
model: function() {
return [{'title': 'first'}, {'title': 'second'}];
}
});
This works really well. However if I navigate to another path and then back again, the settings on the ContentController don't persist. What happens is that the model data gets reloaded, and I assume a ObjectController gets created for each of the list items. Incidentally this is not the case for the ContentsController, which keeps its settings.
What is the solution to preventing ember of creating new ContentController for every list item every time I access the page?
I'm assuming your reference to ContentController is really ContactsController since you are using itemController="contact" in your #each block.
What kind of data are you trying to persist? The showDetails flag? The ContactControllers are going be created and destroyed anytime you exit / enter the route and there isn't anyway I know of to keep those around.
The ContactsController keeps its properties because its a singleton controller generated because you have a ContactsRoute.

ember.js load data in dialog

i try to create my first ember.js app. A calendar-
my day model
App.Day = Ember.Object.extend({
today : null,
dayNumber : null,
addEvent : function() {
console.log(this);
$("#myModal").modal('show');
}
});
the html view
<div class="cal">
{{#each App.DayList}}
{{#if this.today}}
<div class="day today" {{action "addEvent" target="model" }}>
{{#with this as model}}
<span class="text">{{this.dayNumber}}</span>
{{/with}}
</div>
{{else}}
<div class="day" {{action "addEvent" target="model" }}>
{{#with this as model}}
<span class="text">{{this.dayNumber}}</span>
{{/with}}
</div>
{{/if}}
{{/each}}
</div>
so on click on day i show the bootstrap dialog and I wont to load extern data, but I need a information about clicked day.
My understanding is I create a view
App.DayDetails = Ember.View.extend({
});
and inside this view I send an ajax request, but how to get information about clicked day inside this view?
You should almost never be doing any AJAX in a view.
Views do two things:
(1) draw themselves
(2) respond to UI events (clicks, typing, etc)
Your view should get its contents from a controller, in this case I suppose App.DayController or DayDetailsController. (that's another thing, it's best practice to end your subclasses with View or Controller, etc, so its obvious at a glance what they do).
Where the controller gets that data from is where things might get complicated. Ideally, in a mature app, you'd have a data store (a combination—in concept—of your server-side database and ActiveRecord, if you use rails) that would be queried. Simplistically, however, you could have the controller be responsible for using jQuery to manually handle an ajax request. So long as we're taking short-cuts, you could put such a call in a number of place, (a singleton controller, a day-specific item controller, the day model itself), just NOT the view. And it's important when taking these sorts of short-cuts to limit the contagion... all you should be doing with the manual ajax is fetching the JSON and then immediately and expeditiously getting it back into the ember ecosystem by setting it as the content of an array controller. I.e., no going one or two steps further by trying to insert the data into a view manually or whatnot. Don't fight Ember, if you can avoid it.
A few things:
(1) Your use of this is superfluous, as are the {{with}} statements. Inside an {{each}} block the context will be the current object (or its wrapping controller, if you're using itemController) in the iteration. (UNLESS you use "x in y" syntax, in which case the context remains the controller)
(2) The model should NOT be attempting to modify the DOM. Instead, rely on bindings and your controllers to coordinate UI changes. What you might want to do is have a App.DayController that you can put addEvent on, and then in your {{each}} use itemController="App.DayController".
App.DayController = Ember.ObjectController.extend({
addEvent: function () {
// ...
}
});
Then, the context for each loop in your {{each}} template will be each individual day controller. The controller will automatically be the target and context for the views so your template would look like this:
{{#each App.DayList itemController="App.DayController"}}
<div {{bindAttr class=":day today"}} {{action addEvent}}>{{dayNumber}}</div>
{{/each}}
(the : in :day means that day will always be a class, but today will only be a class if the today property on the context is truthy)
Because each day sends addEvent to its own controller, there's no need for figuring out what day to load.