Ember.js: Ensure linkTo helper retains active class on a nested route - ember.js

Take the following route:
#resource 'settings', path: '/settings', ->
#route 'alerts'
and the following link:
{{#linkTo settings.index}}Settings{{/linkTo}}
Is there a specific way in Ember to ensure that the above link is .active as long as I am at a settings.* route?

I'm pretty sure that if you instead use:
{{#linkTo "settings"}}Settings{{/linkTo}}
it will stay active as long as you are within the settings.* route. The above link will route to settings.index anyway.

Related

How to render nested route in new page Ember js?

I currently trying use LinkTo to render another nested route by using {{outlet}} tag to another new page.
I have a forum route and nested forum details route
In the forum template:
<LinkTo #route="main.forum.forum-details" #model={{post}}>{{post.title}}</LinkTo>
{{outlet}}
As the image above. The nested route will be render at the bottom instead of to the new page. How am i going to render it to new page? I was thinking LinkTo will actually link it to the new page right? And the forum details should be render at the {{outlet}} tag, so where should I place the {{outlet}} tag in order to let it render to new page?
You problem is that you need to understand nested routes.
If your route is main.forum.forum-details this means your router looks like this:
this.route('main', function() {
this.route('forum', function() {
this.route('forum-details');
});
});
So the forum route is a parent route of the forum-details route.
And its important to understand that parent routes are always visible when visiting a child route.
So for the main.forum.forum-details ember will render your application route and inside its {{outlet}} it will render the forum route and inside this {{outlet}} it will render the forum-details route.
So if you want either the forum or the forum-details route you can restructure your routes:
this.route('main', function() {
this.route('forum');
this.route('forum-details');
});
or you can move what you currently have in your forum route to your forum.index route. If a route has subroutes and none of the subroutes is active there will always be an index route active that you can use.

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.

How to add active class only for exact routes and not parent routes in ember js?

I have two routes which are nested like this.
router.js
this.route('profile', function() {
this.route('edit');
});
and couple of navbar links for these routes like this..
navbar.hbs
{{#link-to 'profile' tagName="li"}}<a href>View Profile</a>{{/link-to}}
{{#link-to 'profile.edit' tagName="li"}}<a href>Edit Profile</a>{{/link-to}}
The link-to helper adds active class to the li tag here. So when I am in profile route, the first link has active class and when I am in profile.edit route, both the links have active class. (apparently because both the routes get activated when profile.edit is visited.)
How can I avoid the parent route link to get the active class when in a child route?
Basically I don't want the first link (to profile) to have active class when in profile.edit route.
I figured it out if anyone else is facing same issue.
I just changed the link to profile and made it to explicitly profile.index.
navbar.hbs
{{#link-to 'profile.index' tagName="li"}}<a href>View Profile</a>{{/link-to}}
{{#link-to 'profile.edit' tagName="li"}}<a href>Edit Profile</a>{{/link-to}}
This way, When in profile.edit route, the first link does not get the active class.

How can I set a link to route with a dynamic segment

How can I set a link to route with a dynamic segment. According to guide I start with this
window.App = Ember.Application.create()
App.Router.map ->
#resource 'products'
#resource 'product', path: '/product/:product_id'
in my template:
{{#linkTo "product.1"}}products{{/linkTo}}
Unfortunately this gives me the follwing error:
Uncaught Error: assertion failed: The attempt to linkTo route 'product.1' failed.
The router did not find 'product.1' in its possible routes: 'products', 'product', 'index'
{{linkTo}} expects the route defined in the Router.map, so in according to your mapping it should be simply product.
As for the dynamic segment, you also have to pass an object which will be serialized in the ProductRoute. The serialization in almost all scenarios occur without the developer having to do anything since Ember relies on conventions. In rare cases, one must implement serialize a little differently, but for most cases you don't have to touch it.
If you're using {{linkTo}} inside an {{each}} loop you can do it like this:
{{#each product in controller}}
{{#linkTo product product}}Details{{/linkTo}}
{{/each}}
or
{{#each controller}}
{{#linkTo product this}}Details{{/linkTo}}
{{/each}}
Where the first argument is the route name and the second is your model object. In the first code the object has been also named as product, while in the second it's simply being passed as this, which is the product of the iteration.
If you have an unusual scenario where you have to link to a dynamic route while not using the {{each}} loop, you have to expose the object in the controller (preferred) or view. Then you'd have to do something similar to the following:
App.SomeController = Em.Controller.extend
product: null
App.SomeRoute = Em.Route.extend
###
controller is actually `SomeController` here
model is not being used, and is null, while the actual model being
supplied to the controller is `product`, retrieved from store
###
setupController: (controller, model) ->
product = App.Product.find 1
controller.set 'product', product
return
While your template would be similar to this:
{{#linkTo product controller.product}}Product{{/linkTo}}
How does the route know the id?
Conventions. The route will serialize the object you pass, and expose an object with a single property, which has the name of the model for that route, followed be "_id", which in this case would be product_id, so when you click that link, the app activates the ProductRoute, runs the serialize method creating that id property, which will subsequently be used as the argument of the model hook. That's where you call find passing params.product_id as argument. Then the model returns a promise of that model which will be used by the setupController, exposing the object to the view layer as controller.content or simply controller.

Ember.js router v2 App.Router.map inconsistent in documentation?

I'm creating an app with ember.js. I started on PRE.2 but ended up using ember-data v11 so upgraded to master for ember proper. This meant having to change to the new v2 router interface (which as a side note I think is so much better, so thank you.)
I'm having a couple of problems trying to figure out how it works, I'm deep in the guide but there are a couple of inconsistencies I can't quite get my head around:
1)
It seems there are two different conventions used to configure the route map:
In the 'Templates' section, a match().to() interface is used
App.Router.map(function(match) {
match('/').to('index');
match('/posts').to('posts');
match('/posts/:post_id').to('post');
});
( this method is also used in Tom Dale's gist )
In the 'Routing' section, a resource / route interface is used:
App.Router.map(function() {
this.resource('posts', function() {
this.route('new');
});
});
Here it states that a "resource" should be used for noun routes, and "route" for verb routes.
Then in the "Redirecting to a different URL" section, this noun/verb convention isn't followed:
App.Router.map(function(match) {
this.resource('topCharts', function() {
this.route('choose', { path: '/' });
this.route('albums');
this.route('songs');
this.route('artists');
this.route('playlists');
});
});
My first question is:
Going forward, what is the proper convention for creating routes?
My second question follows on from that and is more relevant to my application:
How do I link from a top level "resource" route to a nested "route" route and pass through the appropriate models?
( there is an example of this in the 'Links' section of the 'Templates' doc, but it pertains to the match().to() interface, and I'm specifically working with the resource/route interface )
Here's my example:
I have created a simple site structure based on streams, a stream consists of details, a set of posts, handles and history. My routing is set up like so:
App.Router.map(function() {
this.resource('streams');
this.resource('stream', { path: '/stream/:stream_id' }, function(){
this.route('details');
this.route('posts');
this.route('handles');
this.route('history');
});
});
My streams route looks like this:
App.StreamsRoute = Ember.Route.extend({
model: function() {
return App.Stream.find();
},
setupController: function(controller, model) {
controller.set('content', model);
}
});
and the template:
<script type="text/x-handlebars" data-template-name="streams">
<ul>
{{#each stream in controller}}
<li>{{#linkTo "stream" stream}} {{stream.title}} {{/linkTo}}</li>
{{/each}}
</ul>
</script>
My stream (singular) route:
<script type="text/x-handlebars" data-template-name="stream">
<nav>
{{#linkTo "stream.details" }}Details{{/linkTo}}
</nav>
{{outlet}}
</script>
Now, I'd like to link to my sub route "details", but I'm not sure what to place in the linkTo so that my model (which is a stream) is passed down into this sub-route:
App.StreamDetailsRoute = Ember.Route.extend({ });
My "details" template just displays some attributes off the stream object.
<script type="text/x-handlebars" data-template-name="stream/details">
<h2>Stream Details</h2>
<p>Id: {{id}}</p>
<p>Title: {{title}}</p>
</script>
I will also want to link through to posts, history and handles sub-routes and be able to display these aggregations based on the stream model. I'm not sure exactly how to do this. I assume I need to use setupController to get the items to display, I'm just not sure how to get the stream object down into these sub routes.
Going forward, what is the proper convention for creating routes?
The Resource/Route approach as described in http://emberjs.com/guides/routing/defining-your-routes/
How do I link from a top level "resource" route to a nested "route" route and pass through the appropriate models?
Specify the name of a route as the first parameter, followed by any contexts that are required. So in your example, when creating a link to "stream.details" from the stream template you need to specify this as the context.
{{#linkTo "stream.details" this}}Details{{/linkTo}}
The approach described in http://emberjs.com/guides/templates/links/ still covers the basics.
When in doubt I recommend checking the test cases for link_helper. For example: https://github.com/emberjs/ember.js/blob/master/packages/ember/tests/helpers/link_to_test.js#L249