Search code examples
javascriptbackbone.jsbackbone-routing

Adding routes to Backbone.js Router including 404 handler


This question is similar to 9595182 which explains how to programatically add routes. This is great, however I am trying to add a catch all handler. When I do I get a 404 for all routes.

Workspace = Backbone.Router.extend({

  routes: {
    "help": "help",
    "search/:query/p:page": "search",
  },

  help: function() {
    console.log("help");       
  },

  search: function(query, page) {
    console.log("search",query,page);
  }

});

app = new Workspace();
Backbone.history.start();

//This works
app.route("page/:number", "page", function(number){
    console.log("page called! " + number);
});

//This returns a 404 for everything
app.route("*notFound", "page", function(){
    console.log("404 error");
});

app.navigate('page/4',{trigger:true});
app.navigate('page/3',{trigger:true});
app.navigate('oohh404',{trigger:true});

Here is my jsfiddle


Solution

  • As you've probably noticed, the route() function adds a route to the beginning of Backbone's routes, rather than the end, meaning that each new route you add will be checked before the last one you added. Specifically, when you call route() it ends up calling Backbone.History.route() which adds the route to the beginning of it's handlers array:

    route: function(route, callback) {
      this.handlers.unshift({route: route, callback: callback});
    },
    

    So really a "catch all" route would need to be added first if you add all your routes dynamically (through the functions) or last in the routes object when you define your router. Doing this would allow Backbone to check if each route matches before reaching the "catch all" route you'd want to run only if all other routes don't match.

    But this probably isn't what you wanted to hear. If you really want to add this route to the end after defining a set of routes, you'll need to access the handlers array within Backbone.History to instead push a new route to the end rather than unshifting it to the front of the array. You'll also need to duplicate the function that Backbone has done in their route() function for Backbone.Router.

    This is probably not recommended though, since you'll be accessing internal function of Backbone which could change in the future. You might try adding a feature request to the Backbone codebase, or doing it yourself and issuing a pull request.