Backbone routing allows us to route to different pages.
var Workspace = Backbone.Router.extend({
routes: {
"help": "help", // #help
"search/:query": "search", // #search/kiwis
"search/:query/p:page": "search" // #search/kiwis/p7
},
help: function() {
...
},
search: function(query, page) {
...
}
});
My question is instead of writing different functions for different routes, why not write a single function for all the routes and use a switch statement to determine the exact route and performing tasks based on the route.
It would look something like this.
var Workspace = Backbone.Router.extend({
routes: {
"help": "main", // #help
"search/:query": "main", // #search/kiwis
"search/:query/p:page": "main" // #search/kiwis/p7
},
main: function() {
...
switch(){
case("help") : ...;
case("search") : ...;
}
}
});
I don't know the exact implementation. I just gave a brief idea. Is this possible in Backbone routing?
Because that will lead to a nightmare hell as soon as you have more than 2 o 3 routes/functions, or you need anything more that 2 lines to setup the data and views for each route.
Also, it's much much easier to test your route handlers if you can simply call one function.
If you need one function per your requirements, then what's wrong is your route definition! I assume you are modeling a single page with search functionality and pagination of those search results. Let's suppose that page is accesed with a url like "yourapp/#page":
Enter optional parameters my friend: :)
http://backbonejs.org/#Router-routes
var Workspace = Backbone.Router.extend({
routes: {
"page(/search/:query)(/:page)": "main"
},
main: function(query, page) {
if(query) {
//you're searching
if(page) {
//display specific page
}
else {
//show first results page
}
}
else {
//show you initial views/models
}
}
});
That route will handle: page, page/search/apples and page/search/apples/4
Related
I have a use case and I’m not sure if there is an easy solution within the current Ember router or not, is there a way to define an optional routing param?
I would like to have a dynamic route that might have 1 or might have 2 segments, and then further routes nested inside. My idea of the structure would be something like the below (except the :topic part would be optional)
this.route('course', { path: '/:course' }, function() {
this.route('page', { path: '/:topic/:page' }, function() {
this.route('menu', function() {
});
});
});
/my-course/my-topic/my-page would hit the page route
/my-course/my-page would hit the page route
/my-course/my-page/menu would hit the menu route
/my-course/my-topic/my-page/menu would hit the menu route
One solution I have found is to use the wildcard and then break the segments down myself.
this.route('course', { path: '/:course' }, function() {
this.route('page', { path: '/*path_for_page' }, function() {
this.route('menu', function() {
});
});
});
I am having a trouble with my router and controller. On my app's before:start, I have a handler that fetches collections of Leads and Vehicles.
I have a single region, with my layout view as:
var App = new Marionette.Application({});
var AppLayoutView = Marionette.LayoutView.extend({
el: 'body',
regions: {
main: '#app-container'
}
});
My controller is:
var Controller = Marionette.Object.extend({
leads: function() {
App.regions.main.show(new App.leadsTableView({collection: App.leads}));
},
vehicles: function() {
App.regions.main.show(new App.vehiclesTableView({collection: App.vehicles}));
}
});
In my start handler:
App.on('start', function() {
App.regions = new AppLayoutView();
App.router = new Marionette.AppRouter({
controller: new Controller(),
appRoutes: {
'leads': 'leads',
'vehicles': 'vehicles'
}
});
Backbone.history.start({pushState: true});
});
App.start();
How can I start with a specific route? And, when a user goes to #vehicles, how can I make the region load the new view? I'm missing something about routers.
EDIT: When I go to, #leads in my URL, my vehicles view comes up. When I click on links that go to #leads and #vehicles, they don't work.
Default route
You can define a default by adding a "splat" route (one that starts with *) to the end of your routes. I like to use *default to make the intent obvious:
appRoutes: {
'leads': 'leads',
'vehicles': 'vehicles',
'*default': 'leads'
}
Broken links
Because you are using pushstate routing, the view URL is /vehicles rather than the hash fragment #vehicles. You should no longer use hash fragment urls.
Here's a simple approach to trigger pushState routes with link clicks:
$('a[href]').on('click', function(e) {
e.preventDefault();
var href = e.target.getAttribute('href');
App.router.navigate(href, { trigger: true })
});
You may find this post about moving from hash fragment to pushState routing useful.
You'll also need to configure your server to pass requests that match your route to the main app page - for example, it needs to understand that http://localhost/app/vehicle should be handled by http://localhost/app.
Is there a way to have multiple dynamic segments with a single resource? My use case is to avoid letting the user hit index routes.
Example:
this.resource('tracks', { path: 'albums/:album_id/tracks/:tracks_id' });
And I'd like to avoid the user from hitting the following routes:
albums/:album_id
albums/:album_id/tracks
albums/:album_id/tracks/:track_id
Routes:
this.resource('albums', { path: 'albums' }, function(){
this.resource('album', { path: '/:album_id' }, function() {
this.resource('tracks', { path: 'tracks' }, function(){
this.resource('track', { path: '/:track_id' });
});
});
});
Any help would be greatly appreciated.
Defining Your Routes
NOTE: If you define a resource using this.resource and do not supply a
function, then the implicit resource.index route is not created.
It would be better to use Ember's nested routes. Each route having its own dynamic segment.
App.Router.map(function () {
this.resource('albums', { path: '/albums' }, function () {
this.resource('album', { path: ':album_id' }, function () {
this.resource('tracks', { path: 'tracks' }, function () {
this.resource('track', { path: ':track_id' });
});
});
});
});
If you want to show the user the first track immediately after clicking an album, you could use a redirect.
App.AlbumRoute = Ember.Route.extend({
afterModel: function (album, transition) {
this.transitionTo('track', {album_id: album.id, track_id: album.tracks[0].id});
},
});
Check out the docs on redirection: http://emberjs.com/guides/routing/redirection/
Just for completeness sake, the index routes aren't necessary, they are just a freebie convenience if you define them, if you don't define them it won't go to them.
http://emberjs.jsbin.com/eMofowUQ/1/edit
And you can define multiple slugs in a single path and go directly to it, just note you'll only have a single model for that single resource, so you'll have to deal with that.
http://emberjs.jsbin.com/eMofowUQ/2/edit
A possible solution for us was to use the following:
App.AlbumsIndexRoute = Ember.Route.extend({
redirect: function(){
this.transitionTo('dashboard');
}
});
I need to get this to work:
routes: {
':product' : 'showProduct',
':product/:detail': 'showProductDetail'
showProductDetail never gets called while the ':product' route is set even if it is set afterwards. I tried the following
routes: {
':product(/:detail)': showProductOrDetail
}
But this will not get called when only the second parameter changes.
It is important that I have the product itself or the product and detail in the url.
Does anyone know how to fix this?
There's a little hacky solution to your problem. I have a feeling there is a nicer way to do this but that should work:
routes: {
"product/:id": "showProduct",
"product/:id/details/:did": "showDetails"
},
showProduct: function(id) {
this.showDetails(id);
},
showDetails: function(id, did) {
// Check did for undefined
}
A late response (over a year).. but you can use RegEx in a backbone router to achieve this.
My example presumes the parameters are going to start with a number.
ie: localhost:8888/#root/1param/2param
var router = Backbone.Router.extend({
initialize: function () {
// Use REGEX to get multiple parameters
this.route(/root/, 'page0');
this.route(/root\/(\d+\S+)/, 'page1');
this.route(/root\/(\d+\S+)\/(\d+\S+)/, 'page2');
},
page0:function(){
console.log("no id");
},
page1:function(id1){
console.log(id1);
},
page2:function(id1,id2){
console.log(id1);
console.log(id2);
}
});
Hope this helps.
This is my routes object in a BackboneJS app:
routes: {
"" : "_navigate",
"home" : "_navigate",
"blog" : "_navigate",
"photos" : "_navigate",
"notes" : "_navigate",
"about" : "_navigate",
"singlepost_:id" : "_navigate"
},
it redirects routes to the _navigate method, which looks like this:
_navigate: function(postId) {
if (postId) {
// show single entry
return;
}
// show regular entry
},
It works perfectly fine. However, I find the repetitive routes object to be annoying.
My question is: Is there a better way to direct all these routes to the same method without repeating yourself so much?
Thanks!
http://backbonetutorials.com/what-is-a-router/ Check out the section on splats
Any "*splats" or ":params" in route definitions are passed as
arguments (in respective order) to the associated function. A route
defined as "/:route/:action" will pass 2 variables (“route” and
“action”) to the callback function. (If this is confusing please post
a comment and I will try articulate it better) Here are some examples
of using ":params" and "*splats"
routes: {
"/posts/:id": "getPost",
// Example
"/download/*path": "downloadFile",
// Download
"/:route/:action": "loadView",
// Load Route/Action View
},
getPost: function( id ){
alert(id); // 121
},
downloadFile: function( path ){
alert(path); // user/images/hey.gif
},
loadView: function( route, action ){
alert(route + "_" + action); // dashboard_graph
}
Quite simple, really.
routes: {
"*actions:_id": "_navigate"
}
Thanks to Jason Strimpel from the BackboneJS Google Group.