I'm starting out on Ember.js and I have a three level route for one of the pages. This is what the router map looks like:
App.Router.map(function(){
this.resource('tests');
this.resource('create', function() {
this.resource('create.questions', {path: ':test_id' }, function() {
this.resource('create.questions.question', {path: ':question_id'});
});
});
});
In my CreateRoute, I transition onto the create/questions route using the following code:
this.get('controller').transitionToRoute('create/questions', test);
Which works fine, but in my CreateQuestionsRoute, this code doesn't work:
this.get('controller').transitionToRoute('create/questions/question', question);
The error received:
Uncaught Error: Assertion Failed: Error: Assertion Failed: The route create/questions/question was not found
Using the Chrome Ember inspector plugin, I can see the routes are listed as such:
CreateRoute
CreateQuestionsRoute
CreateQuestions.QuestionRoute
This seems like arbitrary behaviour. There isn't much guidance on how to handle multiple nested routes. Some references told me that my route map should actually look like this:
App.Router.map(function(){
this.resource('tests');
this.resource('create', function() {
this.resource('questions', {path: ':test_id' }, function() {
this.resource('question', {path: ':question_id'});
});
});
});
Whereby the route name would automatically be nested (no need for dot notations), but this did not work. Can anyone with Ember wisdom shine some light for me?
Go with this:
App.Router.map(function(){
this.resource('tests');
this.resource('create', function() {
this.resource('questions', {path: ':test_id' }, function() {
this.resource('question', {path: ':question_id'});
});
});
});
The only reason to add namespace a resource is if the resource isn't unique. Which means from any route you can use
this.transitionTo('questions', model);
this.transitionTo('question', modelForQuestions, modelForQuestion);
Example: http://emberjs.jsbin.com/OxIDiVU/636/edit
If you want to keep your namespace, I'd go with camelCase instead of dot notation, since generally the dot means property on the current scope.
Example: http://emberjs.jsbin.com/OxIDiVU/637/edit
Related
I'm using Ember 3, and I am having an issue using the Router service with dynamic segments. In my component, I use the Router Service to transitionTo a child route on a click, but I get this error:
Error: More context objects were passed than there are dynamic
segments for the route: data.images.image
This is in the component js, where I use transitionTo and pass one parameter for the one dynamic segment:
router: service(),
actions: {
navToSubpage() {
// this.image is a single Ember Data record/object
this.router.transitionTo('data.images.image', this.image)
}
},
This is from my router, which has nested routes with one dynamic segment:
Router.map(function() {
this.route('data', function() {
this.route('images', function() {
this.route('image', {path: '/image_id'});
});
});
});
What am I doing wrong? The error doesn't make sense to me in this case.
I was missing a : before image_id in my Router. The router therefore did not recognize image_id as a dynamic segment, so my transitionTo was interpreted as having too many parameters (1 instead of 0). It had nothing to do with the Router Service.
This is the corrected Router:
Router.map(function() {
this.route('data', function() {
this.route('images', function() {
this.route('image', {path: '/:image_id'});
});
});
});
Using the example route defined below how can a link be defined to /post/123/comments?
Router.map(function() {
this.route('post', { path: '/post/:post_id' }, function() {
this.route('edit');
this.route('comments', { resetNamespace: true }, function() {
this.route('new');
});
});
this.route('comments');
});
Since resetNamespace: true is set on comments, the route post.comments does not exist. Otherwise the following would work.
{{#link-to "post.comments" "123"}}Link{{/link-to}}
When trying to link to news using the comment id, the error More context objects were passed than there are dynamic segments for the route: comments occurs.
{{#link-to "comments" "123"}}Link{{/link-to}}
An example of the second case can be seen here: https://ember-twiddle.com/d9f3b5e692573c80e803
Thanks to locks for pointing me in the direction. The specific issue is how to reuse as much of the route and template as possible for different paths.
router.js:
Router.map(function() {
this.route('post', { path: '/post/:post_id' }, function() {
this.route('edit');
this.route('comments', function() {
this.route('new');
});
});
this.route('comments');
});
routes/post/comments.js:
import Comments from '../comments';
export default Comments.extend({
renderTemplate() {
this.render('comments');
}
});
This will extend the existing comments route to reuse the model and actions defined in the base route. The renderTemplate is still necessary to load the comments template instead of the post.comments template.
Since you're resetting the namespace, you are effectively overriding one of the comments route with the other. You're doing something akin to
obj['comments'] = firstRoute;
obj['comments'] = secondRoute;
Route names need to be unique, which they would be if you didn't reset the namespace, as well as the path. If two routes have the /foobar path, then Ember won't know which one to transition to.
If what you want is to reuse parts from other routes, there are ways to do that.
I'm just starting with Ember JS and Ember CLI and trying to figure out this routing issue. I have a group model that has many game models. With the following route, I am able to display games just fine from a group URL:
Router.map(function() {
this.resource("groups", function() {
this.route('show', {path: ':group_id/show' });
});
});
This results in a URL with the form:
http://localhost:4200/groups/1/show
Suppose one of the group names is "wizards". I'd like to to be able to construct a URL in the following form and render all the games that belong to "wizards":
http://localhost:4200/wizards
Any tips are appreciated.
Like #blessenm points out in the comments, your router would change from
Router.map(function() {
this.resource("groups", function() {
this.route('show', {path: ':group_id/show' });
});
});
to
Router.map(function() {
this.resource("group", { path: ':group_name'});
});
The second parameter to this.resource() or this.route() is optional. If you don't pass anything in - it assumes the same name as your route/resource (groups, in your case). If you pass in an object that has a path: key - you are specifying what the url to the route is, including a dynamic segment. See here for Ember documentation on this.
I have a need for deep nesting some routes in ember, I have something like this.
this.resource('wizards', {
path: '/wizards'
}, function() {
this.resource('wizards.google', {
path: '/google'
}, function() {
this.resource('wizards.google.register', {
path: '/register'
}, function() {
this.route('step1');
this.route('step2');
this.route('step3');
this.route('summary');
});
});
});
What I was expecting was as structure like this:
url /wizards/google/register/step1
route name wizards.google.register.step1
route Wizards.Google.Register.Step1Route
Controller Wizards.Google.Register.Step1Controller
template wizards/google/register/step1
but I got this:
url /wizards/google/register/step1 //as expected
route name wizards.google.register.step1 //as expected
route WizardsGoogle.Register.Step1Route
Controller WizardsGoogle.Register.Step1Controller
template wizards/google.register.step1
What I don't get is when does ember stop using capitalization (WizardsGoogle) and start using namespaces (WizardsGoogle.Register). The seemingly inconsistency confuses me. I would have expected either of them.
I met the same things with deep nested resources. Although I didn't know how this happens, what I can tell is that you can always use CapitalizedNestedRoute without namespace, and Ember can recognize it. Although in Ember Inspector it displays "WizardsGoogle.Register.Step1Route".
In your example I defined such route:
App = Em.Application.create();
App.Router.map(function() {
this.resource('wizards', function() {
this.resource('wizards.google', function() {
this.resource('wizards.google.register', function() {
this.route('step1');
this.route('step2');
this.route('step3');
});
});
});
});
App.IndexRoute = Em.Route.extend({
beforeModel: function() {
// Transition to step1 route
this.transitionTo('wizards.google.register.step1');
}
});
App.WizardsGoogleRegisterStep1Route = Em.Route.extend({
model: function() {
// You can see this alert when you enter index page.
alert('a');
}
});
In this example the app will transition to WizardsGoogleRegisterStep1Route with no problem. And if you use container to find route like this:
App.__container__.lookup('route:wizards.google.register.step1').constructor
It will also display App.WizardsGoogleRegisterStep1Route. It's the same as Ember Guide describes. http://emberjs.com/guides/routing/defining-your-routes/#toc_nested-resources And Ember Guide doesn't introduce namespace route.
So I think it's better to according to what Ember Guide suggests (always use CapitalizedNestedRoute). And in my opinion it's easier to define CapitalizedNestedRoute than nested.namespace.route.
Finally, if you really want to use namespace route/controller/template, you can have a look at Ember.DefaultResolver. Check the API to learn how to extend it so container can lookup modules by your own rules.
Routes are "namespaced" inside resources. And resources uses what you call capitalization, where they sort of define a namespace (for routes to use).
So this set of routes:
App.Router.map(function() {
this.resource('posts', function() {
this.route('new');
this.route('old');
this.route('edit');
this.route('whatever');
});
});
Would result in routes with the following name:
PostsRoute
PostsNewRoute
PostsOldRoute
PostsEditRoute
PostsWhateverRoute
Whereas, the following set of routes:
App.Router.map(function() {
this.resource('posts', function() {
this.resource('photos');
this.resource('comments');
this.resource('likes');
this.resource('teets');
});
});
Would result in route with the following names:
PostsRoute
PhotosRoute
CommentsRoute
LikesRoute
TeetsRoute
Also note, that resources within resources don't get "namespaced" to the "parent" resource, so you'll always ever have the form:
{CapitalizedResourceName}Route // for resources
{CapitalizedParentResourceName}{RouteName}Route // for routes
I hope this helps you!
Can some one explain why nested resources require to list the path hierarchy in the route name instead of just the route?
Eg. resource1 > resource1.resource2
Emberjs seems to be all about reducing the amount of code. Is there some usecase for resources I don't see which explains why resources should be defined this way.
I couldn't get my example to work in jsfiddle or jsbin so I hosted it here: http://emberjs.mattmazzola.net/
I was basing my solution from the technique described in this similar StackOverflow question is here: Ember.js pre4 multiple nested routing
Basically, you notice I have a resource 'animals' with sub resources 'cats' and 'dogs'. However, if I just name them 'cats' and 'dogs' respectively the router says "route animals.cats' is not found. Then if I add the 'animals.' prefix to make the nested route 'animals.cats' the url becomes index#/animals/animals.cats which doesn't make sense. Of course we fix this by overriding the path attribute, but I don't understand why Emberjs doesn't do this by default. Am I defining my resources/routes incorrectly and this is a side affect?
In other words, I'm currently doing this:
App.Router.map(function() {
this.resource('products', function() {
this.route('desktops');
this.route('laptops');
});
this.resource('animals', function() {
// the url for this route is bad, but default behavior?
this.resource('animals.cats', function() {
this.route('cat', {path: ':cat_id'});
});
// Why does this require stating the parent route 'animals' again?
this.resource('animals.dogs', {path: 'dogs/'}, function() {
this.route('dog', {path: ':dog_id'});
});
});
});
How can I write routes like this:
App.Router.map(function() {
this.resource('products', function() {
this.route('desktops');
this.route('laptops');
});
this.resource('animals', function() {
this.resource('cats', function() {
this.route('cat', {path: ':cat_id'});
});
this.resource('dogs', function() {
this.route('dog', {path: ':dog_id'});
});
});
});
hmm, i think the second version should work if you have App.AnimalsIndexRoute, App.CatsIndexRoute and App.DogsIndexRoute (and possibly a few other Ember.Routes) defined correctly. could you maybe post the rest of your code here or in a jsfiddle if you still have that problem?