Multiple Routers in a Backbone (maybe Marionette) app - backbone.js

I currently have defined multple routers for my Backbone app (non-Marionette yet).
authRouter = new AuthRouter()
usersRouter = new UsersRouter()
...
# then to use them
authRouter.navigate "auth/login"
usersRouter.navigate "users/changePassword"
As you can see I am using the variables to navigate. So I must know which router to call. Is it possible to somehow organize code into separate classes but when I want to navigate, just call appRouter.navigate "something" instead of needing to know which router is it? Marionette recommends not having big routers but doesn't have a recommended way/example of it

If you look at the annotated source code for Backbone, you'll notice that calling myRouter.navigate is nothing more than a forwarding method call to Backbone.history.navigate
http://backbonejs.org/docs/backbone.html#section-114
So there's no need to keep track of routers for the navigate method. You can call Backbone.history.navigate directly.
Backbone.history.navigate "auth/login"

Related

Must React Router be used if the goal is just to get one or two URL params?

If we use a React app and hope to compare two ways of doing things, by passing in v=1 or v=2
www.website.com/foo/?v=1
must React Router or some npm package be used to get the value? Does React itself has such a mechanism? Or else, we can just use document.location.search and parse it like in the old days?
React doesn't have a built-in mechanism for that, but - why not just parse document.location.search? If you're not looking for the extra functionality, go ahead and do that. There are easy ways built in to handle such things, for example URLSearchParams:
const search = '?my=foo&another=bar' // or document.location.search
const params = new URLSearchParams(search)
console.log(params.get('my'))
console.log(params.get('another'))
Stick this into a component directly or into a util function and import it to use in a component.

Is there a way to stop a Backbone Router?

Is it possible to close or stop a Backbone Router from listening to its defined routes?
I ask because I have been testing Backbone SubRoute (https://github.com/ModelN/backbone.subroute), setting up an application that has many spaces in which a user could have many subapplications running.
As such, I have defined a a main router, subrouter structure that follows roughy:
MainRouter = Backbone.Router.extend
routes:
"":"root"
"spaces/:id/:module(/*subroute)":"invokeModule"
root: () ->
console.log "root trigger"
invokeModule: (id, module, subroute) ->
that = this
GigaApp.module(module).start({nested_root: "spaces/#{id}/#{module}"})
SubAppRouter = Backbone.SubRoute.extend
routes:
"":"app_home"
app_home: () ->
console.log 'at sub app home'
SubApp.on "start", (options) ->
SubApp.router = new SubAppRouter(options.nested_root)
This general structure works from the first time a sub application is initialized for a space, as the MainRouter starts the SubApp, which initializes its router with the correct nested route. Subsequently, other routes defined in the SubAppRouter also trigger fine.
However, if you navigate to a different space (of different id), and navigate back to the first space, this structure breaks because the SubAppRouter already initialized for that space overrides the MainRouter, and no call the start the SubApp is made from the MainRouter.
So, I'm wondering if there is a way to stop or disable unbind the route triggering of a Backbone router.
As of this moment, the answer is NO
Derick Bailey opened this issue on Backbone's repo suggesting some changes to backbone's structure to support such a change: https://github.com/jashkenas/backbone/pull/1630
There followed a discussion of the merits of such a change, where the following point was made:
To get started -- I'm afraid that I don't understand the premise here. There are a couple axioms of routing that contradict this patch:
Routers exist to match URLs to locations in your application.
The whole point of URLs is that they're always reachable -- once you have a URL, you can go back to it (bookmark it, paste it into a browser) at any point.
I also went over the Backbone source code, and there is no indication that the functionality I am thinking about is possible.
I'm curious why you're not using the AppRouter in Marionette? It should solve your problems regarding dividing routes into smaller AppRoute objects...
Have a look at BackboneRails for some screencasts on building large scale apps. It's not only relevant for devs using rails as backend. It gives a great way to layout your app structure in modules (each with their own App Routes). Highly recommendable.

Backbone Events or Routes?

Since backbone provides two ways of responding to certain events, I was wondering what the general consensus. This is a very common situation - I have a link on a page, i can set up the href on the page to route it so the router can call a function to handle it, like so:
HTML
<a href='#posts/2' class='handleInView'>Item 2</a>
JS
var AppRouter = Backbone.Router.extend({
routes: {
"posts/:id": "getPost"
}
});
or I can respond to the event in the View like so:
var MyView = Backbone.View.extend({
...
events: {
"click .handleInView": "open",
},
...
open: function() {
...
}
});
I know routes provide you with the added benefit of history and direct links, but from a performance standpoint and code layout perspective what is a better approach if I dont care about history.
My routes could be a single place where i can see all of the interactions but it also could get cluttered very quickly.
If you don't care about history or bookmarks, events have fewer side effects (people won't try to bookmark them and they won't interfere with your history) and they're simpler / faster to implement and handle.
Performance-wise, they're slightly faster as well (but really neither method is slow enough to matter at all).
I agree with the comments. Anything that requires deeplinking, bookmarks etc should be handled by using routes. However if you have something like a TabView, or another view that should be inaccessible from a URL, and is nested in another view, then it might make more sense in dealing with that inside of your view code. As for the clutter, you might want to think about reorganizing your routes into separate files. Here are some examples
Backbone general router vs. separate routing files?
Multiple routers vs single router in BackboneJs
In general, routes are used when you are calling a drastic change in the state of your application, or you would like to maintain a browsing history (via backbone.history) so the user can navigate back & forth between states via the browser buttons.
Ideally, you would use both in different circumstances.
I like to think of it in terms of what is changing on my page. If the general page state is the same, but certain elements are changing or updating, I will use events. If the general page state is changing or if I'm loading a different UI Screen, I will use routes.

How to keep my backbone router DRY

I've got a router which serves up most of my main pages from a single route.
'myclasses/:id':'index',
'myclasses': 'index',
'classes/:id':'index',
'index':''
This all works pretty well, but the problem I'm having is that I need to figure out if the request came from myclasses or from classes as that will return a different result which I pass to my collection.
How can I get the requesting url, preferably without the :id, from within the index function?
try Backbone.history.fragment.

Preparing views when routing to initial route

I have a question about routing. The thing is, that I have trouble figuring out how to properly instantiate and render views when my app start for the first time on some route.
For example:
If a user accesses the app via the route /?#a/b/c
My app consists of different sections with subsections. This means that for the above route to work I have to render the view for section A before rendering and displaying the view for subsection B and at last render and display the view for subsubsection C.
The thing is, that this results in a bunch of ugly code in the various routing handlers
a: function(){
A.render();
}
b: function(){
A.render();
B.render();
}
c: function(){
A.render();
B.render();
C.render();
}
So I'm thinking that I'm approaching the problem the wrong way.
When introducing other Routers as the app grows this becomes even harder to maintain I would imagine.
A solution would be if there were an event being triggered before the callback for a route is called. But I can't find a such in the docs.
So my question is, how are these situations handled properly? Because my solution doesn't scale. Is there a way for a to always fire when visiting a "subroute"?
I haven't found a good way to do this but I'll share what I've done and it may or may not apply to your situation. What I wanted to do was have separate routers that respond to portions of the route but backbone doesn't work that way. Backbone will stop when it finds the first route that matches.
What I've done to handle it is to set up a router like this--notice the custom regex to define the route--hopefully it won't make your eyes bleed
initialize:
{
this.route(/([^\/]*)[\/]?([^\/]*)?[\/]?([^\/]*)?/, "renderViews", this.renderViews);
},
renderViews: function(mainView, subView, subSubView)
{
//here you can do something clever--mainView, subView and subSubView may or may not
// have values but they are the names of the views. route of "a/b" will pass
// ["a", "b", undefined] as your arguments
if (mainView)
(new App.Views[mainView]()).render();
if (subView)
(new App.Views[subView]()).render();
if (subSubView)
(new App.Views[subSubView]()).render();
}
I realize this isn't exactly what you're probably hoping for but it worked well for me in a project.
good luck

Resources