Naming of Routes for nested resources in Ember.JS - javascript

The following route setup is from the Ember.JS docs (http://emberjs.com/guides/routing/defining-your-routes/) and I have to deal with an equivalent problem:
App.Router.map(function() {
this.resource('post', { path: '/post/:post_id' }, function() {
this.route('edit');
this.resource('comments', function() {
this.route('new');
});
});
});
According to the docs and my own experience this results, among others, in the following route:
/post/:post_id/comments -> App.CommentsIndexRoute
However, since I want a post-specific comment route I would have expected
/post/:post_id/comments -> App.PostsCommentsRoute
What exactly is my fallacy and what would I have to change to achieve my goal.

Only route's share their name with their parent resource. If you wanted it to show up as as PostsCommentsRoute it would be more like this (note I pluralized it to match your example, despite the url not being pluralized)
App.Router.map(function() {
this.resource('posts', { path: '/post/:post_id' }, function() {
this.route('comments');
});
});

Related

Meteor.js Iron Router and similar or dynamic routes

I am trying to build dynamic routes for an admin section on my site so that "/admin" would work as well as "/admin/users" and "/admin/users/add" and so on. I have tried some different combinations but still struggling with this. Below is what I've tried and in different orderes.
Ideally if I could just specify "/admin" but dynamically reference each new / as an argument that would be best for handling in the code. Example "/admin/1/2/3/4/5" and being able to reference the 1 2 3 etc. I didn't see anything like this in the docs though.
Router.route('/admin', {
name: 'admin',
path: '/admin',
template: 'admin',
layoutTemplate: 'layout_admin',
action: function() {
Session.set('apage', 'dashboard');
Session.set('asect', null);
this.render();
}
});
// Not Working...
Router.route('/admin/:apage', {
name: 'admin',
path: '/admin/:apage',
template: 'admin',
layoutTemplate: 'layout_admin',
action: function() {
Session.set('apage', this.params.apage);
Session.set('asect', null);
this.render();
}
});
// Not Working...
Router.route('/admin/:apage/:asect', {
name: 'admin',
path: '/admin/:apage',
template: 'admin',
layoutTemplate: 'layout_admin',
action: function() {
Session.set('apage', this.params.apage);
Session.set('asect', this.params.asect);
this.render();
}
});
EDIT (Answered)
After some testing it seems calling a template should be (or easiest done) in the this.render() line and the routes should go from most restrictive/detailed to least - which I did try before. The problem seems to be using this.params on the template: line. This solution is not perfect but posting for anyone who may run into a similar problem. As far as further variables in the url like "/admin/1/2/3/4/5" it seems they would need additional routes and can't be fully dynamic as a "/" can not go into the params and the router would look for a route and return notFound unless you can an explicit matching route. There may be a work around that I did not find.
Working code below:
Router.route('adminPage', {
path: '/admin/:asect/:apage',
template: 'admin',
layoutTemplate: 'layout_admin',
action: function() {
Session.set('asect', this.params.asect);
Session.set('apage', this.params.apage);
this.render('admin_' + this.params.asect + '_' + this.params.apage);
}
});
Router.route('adminSect', {
path: '/admin/:asect',
template: 'admin',
layoutTemplate: 'layout_admin',
action: function() {
Session.set('asect', this.params.asect);
Session.set('apage', null);
this.render('admin_' + this.params.asect);
}
});
Router.route('admin', {
path: '/admin',
template: 'admin',
layoutTemplate: 'layout_admin',
action: function() {
Session.set('asect', 'dashboard');
Session.set('apage', null);
this.render('admin_dashboard');
}
});
There is a way to have optional parameters in routes (which is what you're looking for unless I'm mistaken). With that in mind, you should be able to manage using one router.
Router.route('admin',{
path: '/admin/:asect?/:apage?',
template: 'admin',
layoutTemplate: 'layout_admin',
action: function() {
var asect = this.params.asect || 'dashboard',
apage = this.params.apage || null,
render = (function(){
if(apage !== null) {
return 'admin_'+ asect +'_'+ apage;
} else {
return 'admin_'+ asect;
}
})();
Session.set('asect', asect);
Session.set('apage', apage);
this.render(render);
}
});
The ? after each parameter in the path designates it as an optional parameter. You should be able to then check if it has been defined or otherwise assign a default value and then structure your view and session accordingly.
Note: You can test in this MeteorPad - just update the URL according to the names of the example templates.
http://meteorpad.com/pad/Ri4Np5xDJXyjiQ4fG

Templates on dynamic segments

I have a requirement to make what is essentially a dynamic form (wizard) that has multiple steps. Now I want to be able to quickly add new steps to the wizard in the future (or remove them) so I don;t to create separate routes for each step like so:
this.resource('wizard', { path: '/' }, function() {
this.route('step1', { path: '/' });
this.route('step2');
this.route('step3');
this.route('step4');
this.route('step5');
});
I would much prefer to have a dynamic segment that takes in the name of the step and loads the corresponding template of the same name, like so
this.resource('wizard', { path: '/' }, function() {
this.route('step', { path: '/:step' });
});
Is this at all possible or is this just wishful thinking.
I have come up with a solution but I am not sure it is considered the best...
I have defined a route in the router to take in a dynamic segment with the name of the template:
this.resource('wizard', { path: '/wizard' }, function() {
this.route('missing', { path: '/:step' });
});
I have then created a missing route that takes this dynamic segment from the model and uses it to load in the template into the appropriate outlet
export default Ember.Route.extend({
renderTemplate: function(controller, model) {
this.render('wizard/' + model.step, {
controller: controller
});
}
});
I would love to hear some thoughts on this solution.

Ember.js Won't Accept Root URL

I am trying to create an Ember.js app on a domain that has a ton of different sublevel sites. I'm trying to set my rootURL to '/org/new/' like the docs say so that the root URL will be mydomain.com/org/new/. I'm using the following router code:
App.Router.reopen({
rootURL: '/org/new/'
});
App.Router.map(function() {
this.resource('carouselItems', { path: '/' }, function() {
// child routes
});
});
App.CarouselItemsRoute = Ember.Route.extend({
model: function() {
return this.store.find('carouselItem');
}
});
However, my app keeps trying to load mydomain.com/carouselItems instead. I have a CarouselItemsController that extends ArrayController and a CarouselItemController that extends ObjectController. Both are used when the user navigates to mydomain.com/org/new/ to generate some items in a Bootstrap carousel.

Ember.js Error in specifying URL type of location='history' in App.Route

I have started learning the ember.js framework and I am stuck at how to use the setting of the URL type feature that the framework has.
http://emberjs.com/guides/routing/specifying-the-location-api/
I have this simple application.js
App = Ember.Application.create();
App.Router.reopen({
location: 'history'
});
App.Router.map(function () {
this.route('about');
});
App.ApplicationRoute = Ember.Route.extend({
model: function () {
return appdata;
}
});
App.IndexRoute = Ember.Route.extend({
setupController: function (controller) {
// Set the IndexController's `title`
controller.set('indextitle', "My Index title");
},
renderTemplate: function () {
this.render({ outlet: 'indexoutlet' });
}
});
App.AboutRoute = Ember.Route.extend({
model: function () {
return appdata;
},
renderTemplate: function () {
this.render({ outlet: 'aboutoutlet' });
}
});
var appdata = { mytext: '', theplaceholder: 'Enter new text', attr:'Yeap!' }
If I don't use the
App.Router.reopen({
location: 'history'
});
the application works fine and it goes to the 'about' route by appending the URL the '~/EmberjsTest.aspx#/about' as it supposed to do.
However because I do not like the hash symbol in the URL of the page, I would prefer if it was removed and to do that the guide says we should put this code:
App.Router.reopen({
location: 'history'
});
But when I do it I get an error in the Chrome console saying:
'Assertion failed: The URL '/EmberjsTest.aspx' did match any routes in your application'
What am I doing wrong?
Thanks in advance
If you want to use the history API then you have two options.
Serve your Ember app from '/' so that Ember can just work with it's "normal" index/root route.
Create a route in your Ember app that can handle '/EmberjsTest.aspx'.
this.route("index", { path: "/EmberjsTest.aspx" });
Note that if you go with option 2 you'll probably have to update all of your routes to include '/EmberjsTest.aspx' in their paths.
this.resource("posts", {path: "/EmberjsTest.aspx/posts" })

Backbone.js trigger function each time Router is used

Just wondering if there is a simple way to trigger a custom function each time a Backbone.js router is utilized, without having to add it in each of the router functions. Now my script looks like this:
var AppRouter = Backbone.Router.extend({
routes: {
'' : 'index',
'test': 'test',
},
initialize: function() {
},
index: function() {
customFunction();
indexView.render();
},
test: function() {
customFunction();
testView.render();
},
});
I would like to have it something like this:
var AppRouter = Backbone.Router.extend({
routes: {
'' : 'index',
'test': 'test',
},
initialize: function() {
},
index: function() {
indexView.render();
},
test: function() {
testView.render();
},
change: function() {
customFunction();
}
});
Does anyone have any suggestions?
Whenever a route is matched by the Router, a route:[name] event is fired with the name of the route matched, to allow classes to listen to certain route matches. All backbone objects also support the all event which fires whenever an event does.
So you could make use of this to bind to the all event on the Router, which will fire whenever the router does some routing.
initialize: function() {
this.bind( "all", this.change )
},
If you were interested in the route that matched, it is passed as the first parameter to the bound function.
More details here in the FAQ
A more (new) Backbone way of writing the bind is:
initialize: function() {
this.listenTo(this, "all", this.change )
},
a better listen would be just route if you ask me; Because all triggers two events, i think these:
"route:[name]" (params) — Fired by the router when a specific route is
matched.
"route" (route, params) — Fired by the router when any route
has been matched.
I am new to backbone. Following Laurens answer, I managed to call on each route change with below
app_router.on('route', function(e){
console.log(e);
});

Categories