Add standard routes with methods on the router - backbone.js

In Backbone.Marionette documentation some statement puzzled me:
You can also add standard routes to an AppRouter with methods on the router.
(https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.approuter.md)
How it might look like in reality?

You can add a route on the AppRouter and define the method to handle it in either the router or controller.
MyRouter = Backbone.Marionette.AppRouter.extend({
// "someMethod" must exist at controller.someMethod
appRoutes: {
"some/route": "someMethod"
"yet/anotherRoute": "routerMethod" // app router route
},
/* standard routes can be mixed with appRoutes/Controllers above */
routes : {
"some/otherRoute" : "someOtherMethod"
},
// method on the router
routerMethod: function() {
// ...
},
someOtherMethod : function(){
// do something here.
}
});

Related

Backbone Route not firing

This should be super basic but I can't get routing working. I should mention that the application is located in a subdirectory called /dist/. Here's my code:
var QuestionRouter = Backbone.Router.extend({
routes: {
"/dist/" : "startTest"
"dist/:id": "getModel"
},
startTest: function(){
console.log('home called')
},
getModel: function(){
app.getModel(id);
}
});
var app = new QuestionView;
var appRouter = new QuestionRouter;
Backbone.history.start({pushState: true});
The URL to trigger this route is:
www.example.com/dist/
www.example.com/dist/12345
Any help would be appreciated.
You'll need to use # (hash symbol).
Backbone routers are used for routing your applications URL's when
using hash tags(#)
This is a quote from a Backbone tutorial: What is a router?
See Backbone's Router documentation
Then your routes would be:
www.example.com/#/dist/
www.example.com/#/dist/12345
You can also use Backbone routes without hashes.
Ok So I was able to work this out:
My route hash should look like this:
routes: {
"" : "startTest",
":id": "getModel"
}
I had to remove pushState: true, with this in place the route wasn't being triggered, not sure why:
Backbone.history.start();

Non index root path with Marionette router

I have to serve up my backbone app on the "/b" route and am having trouble hooking in my router. It works fine if I just showView a view, but my route controller functions are not firing when I hook in my router, any thoughts?
router:
define('appRouter', ['marionette', 'rootView', 'changePasswordView'], function(Marionette, rootView, changePasswordView) {
return Marionette.AppRouter.extend({
routes: {
'/b/change-password': 'showChangePassword',
'/b': 'showAccountSettings'
},
showChangePassword: function() {
this.showView(new changePasswordView());
},
showAccountSettings: function() {
this.showView(new rootView());
}
});
});
application onStart (which is confirmed firing):
var Application = Marionette.Application.extend({
...
onStart: function(options) {
console.log('on start');
var router = new appRouter(options);
/** Starts the URL handling framework */
if( ! Backbone.History.started) Backbone.history.start();
router.initialize();
},
...
});
When I visit http://localhost:8080/b (which is for all intensive purposes my index) it renders a blank page.
Default routes in Backbone are hash-based. Link to Your /b route should look like http://localhost:8080/#/b.
If You don't need hash-based links, start history with pushState: true.
Backbone.history.start({pushState: true});
EDIT:
If You serve app on /b path, then You have wrong defined routes. Routes must be defined relative to /b:
routes: {
'change-password': 'showChangePassword',
'': 'showAccountSettings'
},
And access:
http://localhost:8080/b' -showAccountSettings`
http://localhost:8080/b#change-password' -showChangePassword`

Routing in Backbone.js / Marionette.js - no hashtags, route list and sub-routers

I have three questions about routing in Backbone.js / Marionette.js :
1) How can I get a list of all the routes my application's routers have registered ?
For example for Express.js (in Node.js) it would be app.routes.
I'm trying to do the same with Backbone.js / Marionette.js but couldn't find any property or method that did this.
2) I want to clean-up my URLs and remove the hashtag "#" in front of them, I know that they trigger the Routers so how can I manage to do this ?
I found the following script that prototypes the Backbone router, but it's more of a hack than a stable solution : Simple backbone routing without hash URLs
3) Is is possible to have sub-routers in Backbone.js / Marionette.js ?
What I mean by sub-router is a router which only handles a part of a url, e.g :
var AppRouter = Backbone.Router.extend({
routes: {
'articles' : 'MyArticleRouter'
}
});
var MyArticleRouter = Backbone.Router.extend({
routes: {
'science' : 'someMethod',
'literrature' : 'someOtherMethod'
}
});
This would categorise my URLs a little bit more by letting me define the main routes in AppRouter and all the subroutes (part after the second slash "/") in category-specific sub-routers.
So for the following URL : "hostname/articles/science", the routing process would look something like this :
1) pass "/articles/science" to AppRouter
2) AppRouter splits the URI and takes the "/articles" part
3) AppRouter finds the registered "/articles" route
4) AppRouter recognises that MyArticleRouter is bound to that URI element
5) AppRouter forwards the routing to that router and only passes the "/science" element as a route
6) MyArticleRouter routes "/science" to the someMethod() and runs it
Thank you in advance !
Answer for #1:
All the routes are registered in Backbone.history.handlers.
Answer for #2:
You can add a handler to every link in your site:
var application = new Backbone.Marionette.Application();
application.addInitializer(function(options) {
// Add class to target a browser, not as standalone app.
if(window.navigator.standalone != true) {
$('body').addClass('no-standalone');
}
// Prevent internal links from causing a page refresh.
$(document).on('click', 'a', function(event) {
var fragment = Backbone.history.getFragment($(this).attr('href'));
var matched = _.any(Backbone.history.handlers, function(handler) {
return handler.route.test(fragment);
});
if (matched) {
event.preventDefault();
Backbone.history.navigate(fragment, { trigger: true });
}
});
});
Of course make sure you use pushState:
if (!Backbone.history.started) {
Backbone.history.start({ pushState: true });
}
That last snippet must be run after you have initialized all your routers.
Answer for #3:
This may work a little to split your routes:
define([
'backbone',
'underscore',
'routers/dashboard',
'routers/anotherroute1',
'routers/anotherroute2'
],
function(Backbone, _, DashboardRouter, AnotherRoute1, AnotherRoute2) {
'use strict';
var application = new Backbone.Marionette.Application();
application.addInitializer(function () {
_.each([DashboardRouter, AnotherRoute1, AnotherRoute2], function(router) {
new router();
});
if (!Backbone.history.started) {
Backbone.history.start({ pushState: true });
}
});
return application;
});

Converting Backbone structures to TypeScript

Looking at Backbonejs' implementation of the extend function, it shows that it isn't a basic prototype extension, and when backbone extensions are converted straight to TypeScript classes some things stop functioning. What is the right way to convert each component of Backbonejs (Model, Collection, Route, View, History) to a typescript class and make sure nothing is broken?
Thanks!
Update:
The answer in this Q/A can also work for other backbone constructs.
This is because the code generated by TypeScript first calls the Router constructor and then declares the routes.
Code generated by TypeScript compiler
...
function AppRouter() {
_super.apply(this, arguments);
this.routes = { // <= routes defined after _super constructor
"*actions": "defaultRoute"
};
}
...
To fix this just call the Backbone Router function _bindRoutes() on your TypeScript object constructor.
Example:
class AppRouter extends Backbone.Router {
routes = {
"*actions": "defaultRoute"
}
constructor(){
super();
// call _bindRoutes() here function to bind your routes
(<any>this)._bindRoutes();
}
defaultRoute() {
document.write("Default Route Invoked");
}
}
var app_router = new AppRouter();
Backbone.history.start();
Download a sample code here

Programmatically adding routes to Backbone.Router?

Here is my application-router.js file where i'm creating Backbone.Router object with just only few routes:
var App = App || {};
App.Router = Backbone.Router.extend({
routes : {
'' : 'showDashboard', // Not shown
'*other': 'showModalError'
},
defaultRoute : function(other) { $('#modal404').modal(); }
});
In main javascript file application.js i'd like to programmatically add routes. I've tried with route() function and it doesn't work, routes are not added. It works however passing an object to the "constructor", but that will override already defined routes:
// This works and overrides all defined routes in App.Router
var router = new App.Router({ routes : { '/test/me' : 'testRoute' } });
// This is not working
router.route(ExposeTranslation.get('customers.new.route'), 'newCustomer');
router.route('/test/me/again', 'testAgainRoute');
In fact console.log(App.Router) shows:
routes Object { /test/me="testRoute"}
I suppose i'm missing something i can't figure out, i'm beginning learning this little piece of powerful javascript.
Your router.route calls are working, those calls aren't your problem. When you call route to add a new route, the new route goes at the end of the routing list. In particular, the routes that are added by your route calls go after '*other' and '*other' will match anything so your new routes will be effectively ignored.
Try removing your '*other' route from routes and adding it after your two route() calls:
routes : {
'' : 'showDashboard' // Not shown
},
router.route(ExposeTranslation.get('customers.new.route'), 'newCustomer');
router.route('/test/me/again', 'testAgainRoute');
router.route('*other', 'showModalError');
The routes aren't stored in App.Router object, they're stored inside Backbone.history:
route: function(route, name, callback) {
// ...
Backbone.history.route(route, _.bind(function(fragment) {
//...
}, this));
return this;
},
That's why your console.log(App.Router) doesn't say anything helpful.

Resources