Re-render template when changing route - ember.js

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.

Related

How show component only in a specific route?

I have a componente called hero (in application.hbs) and I wish display this componente only in home page.
I researched about how do this but without any success. Thanks!
After a few minutes and some searches on GitHub...
Just install ember install ember-truth-helpers and check the route name:
{{#if (eq currentRouteName 'index')}}
{{hero}}
{{/if}}
Glad to help!
I need more specifics, however, I am going to make the assumption that your home route is the '/' route.
The '/' route is actually your index route, so if you create an index.hbs file it will act as the template for your index route. And then your should just move the hero component to your index.hbs file.
I can't be sure your reasons, but I suspect that this could be a solution.
There is an invisible 'application' route... there is also an implicit 'index' route, but you can skip the confusion of that and just create a 'home' route and give it a path to the root. The application template will house the outlet - and then you can place your component just in the 'home' template;
(don't write an application route like this, but just for visualization)
Router.map(function() {
// overarching 'application' route
this.route('application', function() {
this.route('home', { path: '/' });
this.route('other');
});
});
Here is a twiddle with the full example in place. If this doesn't do what you want, then refer to the conditional suggestions. : )
Router.map(function() {
// here's an example of skipping of skipping the mysterious 'index' in another situation
this.route('books', function() {
this.route('books-list', { path: '/' });
this.route('book');
});
});
You can also render a component dynamically using component helper which save you a conditional statement inside your template.
The first parameter of the helper is the name of a component to render, as a string. So {{component 'blog-post'}} is just the same as using {{blog-post}}.
When the parameter passed to {{component}} evaluates to null or undefined, the helper renders nothing. When the parameter changes, the currently rendered component is destroyed and the new component is created and brought in.
So you can safely pass in anything to the component helper, in your case you can make the component name dynamically without worry an error will raised.
https://guides.emberjs.com/v2.1.0/components/defining-a-component/#toc_dynamically-rendering-a-component

link-to works in index.hbs but not in application.hbs

When I do a {{#link-to "cars/category" "mini"}} from index.hbs, everything works fine. I transition to cars/category -> templates/cars/category.hbs.
However, when I do the same from application.hbs (for navigation), I transition to an empty page and then automatically transition to the parent route cars.index -> templates/cars/index.hbs.
There is probably some logic to this. How can I transition to this route from a link click in application.hbs?
(a hard link <a href="/cars/mini" works fine, but I'll lose the state of the application.)
routes/cars/category.js model:
model(params) {
return this.store.findRecord('cars/category', params.category_id, {backgroundReload: false});
}
In route.js:
this.route('cars', () => {
this.route('cars/category', { path: '/cars/:category_id' });
});
For Ember 2.11
Have you try to change the route path to dot notation cars.category
A sample nested routes,
Router.map(function() {
this.route('photos', function(){
this.route('photo', { path: '/:photo_id' }, function(){
this.route('comments');
this.route('comment', { path: '/comments/:comment_id' });
});
});
});
A proper link-to helper link with multiple segements,
{{#link-to 'photo.comment' 5 primaryComment}}
Main Comment for the Next Photo
{{/link-to}}
You can read more at,
https://guides.emberjs.com/v2.5.0/templates/links/#toc_example-for-multiple-segments
For the below route.js
this.route('cars', function(){
this.route('category', { path: '/:category_id' });
});
You can do {{#link-to "cars.category" "mini"}}, this will transition to /cars/mini url.
You don't need to have cars/category. because it's already nested inside cars route.
Created Sample Twiddle .
For better understanding routing see AlexSpeller ember-diagonal
Someone suggested that since I transition to the desired page and then immediately transition to its parent, there must have been some other transition queued.
On further inspection, the link-to was indeed inside a drop down menu of a list-item which itself was a link-to too.
The solution was to add bubbles=false to the inner link-to.
The other answers here doubt the used routes. However, they are fine and setup like this for a reason. E.g. multiple routes with a subroute called category cannot all be in the root. However, it was my fault for not disclosing the exact code which put people on the wrong track, for they would probably have noticed the actual problem immediately.
Next time I will be more verbose in my code. I apologize, and thanks for thinking with me.

Not rendering/removing sidebar based on route

Something I've been experimenting around with Ember for a couple of hours and can't work out. Hopefully it's just a terminology issue that I'm getting stumped on as I read through the Ember docs.
I have an application, that, for the most part, consists of a sidebar/top bar (called wrapper), and a footer.
My basic application.hbs looks like this (I'm using Ember App Kit to provide structure):
{{partial "wrapper"}}
{{outlet}}
{{partial "footer"}}
If this was the state of my application, it would work pretty well. Page content loads in the {{outlet}} fine.
My main issue is how to break out of this template structure in an "Ember" way (and preferably without going all jQuery and removing DOM elements willy-nilly).
I have a few routes that I don't want the wrapper and the footer to show on (they're full page login/forgot password routes, and a couple of minimal interface/no distractions modes).
I experimented with trying to remove the sidebar and footer by making the default template (application.hbs):
{{#if showWrappers}}
{{partial "wrapper"}}
{{/if}}
{{outlet}}
{{#if showWrappers}}
{{partial "footer"}}
{{/if}}
Where showWrappers is in the ApplicationController:
export default Ember.Controller.extend({
showWrappers: function() {
var routes = ['login'],
currentPath = this.get('currentPath'),
show = true;
routes.forEach(function(item) {
var path = new RegExp('^' + item + '*');
if (!Ember.isEmpty(currentPath.match(path))) {
show = false;
}
});
return show;
}.property('currentPath'),
});
Attemping to transition to /login from / using {{link-to}} returns in an error: Uncaught Error: Cannot perform operations on a Metamorph that is not in the DOM presumably because I'm removing things Ember wanted to keep (I am using {{link-to}} and {{bind-attr}} in the sidebar, so there are bindings there).
Aware that I could use actions and jQuery to hide elements of the page and bring them back for the "distraction free" mode, but I'd prefer to learn how to structure templates and use Routes with the renderTemplate hook potentially using this.render (?) to blow away the current DOM and rebuild from a different base (rather than application.hbs).
Thoughts? More than happy to clarify.
I have discovered disconnectOutlet, and have converted my partials into outlets:
{{outlet wrapper}}
{{outlet}}
{{outlet footer}}
Made my ApplicationRoute render to them by default:
export default Ember.Route.extend({
renderTemplate: function() {
this.render();
this.render('wrapper', {
outlet: 'wrapper',
into: 'application'
});
this.render('footer', {
outlet: 'footer',
into: 'application'
});
}
});
and then on the LoginRoute, I just run this.disconnectOutlet for both wrapper and footer, and seems to work pretty well.

Ember.js how to design different representations of Data (with TodoMVC as an example)?

I would like to know what's the best way of designing the display of different representations of the same data model in Ember.js. To ask my question, I'll use the TodoMVC of Ember.JS, which has 3 representations of todo-data:
any todo, i.e. the entire todo list (TodosIndexRoute)
todos that are still active and incomplete (TodosActiveRoute)
todos that have been completed (TodosCompletedRoute)
Currently, you can see each of the 3 by clicking on the words at the bottom of the list, directing to a different URL each time. Since currently each representation has a route, it makes sense that each representation gets its unique URL. The main page displays the entire todo list (1.).
A. What is the best ember.js design to make the main page display all 3 representations (i.e. under just one URL)?
B. How about the best design that displays all 3 on the main page as well as on separate pages?
Currently I only figured out a clumsy way and made this modified TodoMVC app that shows incomplete and completed lists at the bottom of the page.
In index.html, I added new named lists:
{{#each todosactive itemController="todo"}}
{{ title }},
{{/each}}
In the js router, I copied TodosActiveRoute and TodosCompletedRoute into TodoIndexRoute, which is code duplication, very bad.
Todos.TodosIndexRoute = Ember.Route.extend({
setupController: function () {
var todos = Todos.Todo.find();
this.controllerFor('todos').set('filteredTodos', todos);
var todos_active = Todos.Todo.filter(function (todo) {
if (!todo.get('isCompleted')) {
return true;
}
});
this.controllerFor('todos').set('todosactive', todos_active);
...
});
I feel like I'm missing an elegant way of doing this, but my current ember.js knowledge is very limited. Should I use {{partial}}, {{render}}, render, or something else?
I tried {{ partial }} and {{ render }}, but I can't get them to display any data .
Thanks for helping out.
A) Ember tries to work really closely with urls. This is a good thing since if you want to share a url, the view should be consistent. The url is a powerful tool and each unique url should link to the same unique page. Having one url that links to multiple views isn't great, and certainly not shareable. If you have some time listen to some talks by Tom Dale and Yehuda Katz for an interesting overview of ember and what they're trying to do.
B) You can include different views on one page. Have a look at the guides, most notably on rendering templates and using helpers for more information on including different views under one url.
A) To display all 3 representations in one view, we actually just need the basic model in the single route. The key is for the controller to give you flavors of that model. The other important thing is to use data binding in the handlebars template. You can see the running version here.
In the IndexRoute, add a model that gets the plain todos list:
Todos.TodosIndexRoute = Ember.Route.extend({
model: function(params) {
return Todos.Todo.find();
},
...
Make a TodosListView that doesn't need to have anything:
Todos.TodoListView = Ember.View.extend();
In the controller, add 2 computed properties that returns the desired arrays:
Todos.TodosController = Ember.ArrayController.extend({
...
todosActive: function() {
return this.filterProperty('isCompleted', false);
}.property('#each.isCompleted'),
todosCompleted: function() {
return this.filterProperty('isCompleted', true);
}.property('#each.isCompleted'),
...
Finally, in the HTML template:
<script type="text/x-handlebars" data-template-name="todos">
Active:
{{#view Todos.TodoListView lalaBinding="todosActive"}}
{{#each view.lala}}
{{title}},
{{/each}}
{{/view}}
Completed:
{{#view Todos.TodoListView dataBinding="todosCompleted"}}
{{#each view.data}}
{{title}},
{{/each}}
{{/view}}
</script>
note the dataBinding.
Thanks to the folks on #emberjs irc for helping out.

Ember Nested Routing

In my application.hbs want to:
{{#linkTo 'pieces/newPiece' }}New Piece{{/linkTo}}
So I set up the router:
App.Router.map(function() {
this.resource('pieces', function(){
this.route('newPiece');
});
});
Yet I get:
The route pieces/newPiece was not found
Any direction appreciated
UPDATE
I changed:
{{#linkTo 'pieces/newPiece' }}New Piece{{/linkTo}}
to
{{#linkTo 'pieces.newPiece' }}New Piece{{/linkTo}},
and that took care of the error, but what I want is to call the 'newPiece' function of the piecesController.
You want to use the {{action}} helper for that. From the guide:
You may want to trigger high level events in response to a simple user
event (like a click).
In general, these events will manipulate some property on the
controller, which will change the current template via bindings.
In your case, assuming that pieces is the current controller in context you would use:
<a href='#' {{action newPiece}}>New Piece</a>
JSBin example