multiple matching routes

10,313

Solution 1

Short answer: No, you can't do that. One Controller per page.

Long answer: When you instantiate a new Controller, it adds its routes to the History singleton. The History singleton is monitoring the hash component of the URL, and when the hash changes, it scans the routes for the first expression that matches its needs. It then fires the function associated with that route (that function has been bound to the controller in which it was declared). It will only fire once, and if there is a conflict the order in which it fires is formally indeterminate. (In practice it's probably deterministic.)

Philosophical answer: The controller is a "view" object which affects the presentation of the whole page based on the hash component of the URL. Its purpose is to provide bookmark-capable URLs that the user can reach in the future, so that when he goes to a URL he can start from a pre-selected view among many. From your description, it sounds like you're manipulating this publicly exposed, manually addressable item to manipulate different parts of your viewport, while leaving others alone. That's not how it works.

One of the nice things about Backbone is that if you pass it a route that's already a regular expression, it will use it as-is. So if you're trying to use the controller to create a bookmarkable description of the layout (component 1 in the upper right hand corner in display mode "A", component 2 in the upper left corner in display mode "B", etc) I can suggest a number of alternatives-- allocate each one a namespace in the hash part of the URL, and create routes that ignore the rest, i.e.

routes: {
    new RegExp('^([^\/]*)/.*$'): 'doComponent1stuff',
    new RegExp('^[^\/]*/([^\/]*)\/.*$': 'doComponent2stuff',
}

See how the first uses only items after the first slash, the second after the second slash, etc. You can encode your magic entirely how you want.

I suggest, though, that if you're going to be doing something with the look and feel of the components, and you want that to be reasonably persistent, that you look into the views getting and setting their cookies from some local store; if they're small enough, cookies will be enough.

Solution 2

I have a very similar issue. At present, backbone stops after the first matching route. I have a dirty workaround where I am overriding the loadUrl method of Backbone History. Here I am iterating through all of the registered routes and triggering callback for all of the matching routes .

_.extend(Backbone.History.prototype, {
  loadUrl : function() {
    var fragment = this.fragment = this.getFragment();
    var matched = false;
    _.each(this.handlers, function(handler) {
      if (handler.route.test(fragment)) {
        handler.callback(fragment);
        matched = true;
      }
    });
    return matched;
  }
})

Philosophically, I am fine with having single controller per page. However, in a component based view framework, it will be nice to have multiple views per route rendering different parts of a view state.

Comments are welcome.

Solution 3

I've used namespacing to deal with a similar problem. Each module comes with it's own module controller, but is restricted to handle routes that start with /moduleName/ this way modules can be developed independently.

Share:
10,313

Related videos on Youtube

hawkett
Author by

hawkett

Updated on March 15, 2020

Comments

  • hawkett
    hawkett about 4 years

    I've got a backbone.js application that defines two controllers, and the controllers both define route patterns which match the location.hash. I'm having trouble getting both of them to fire - e.g.

    ManagerController = Backbone.Controller.extend({
       routes: {
          ":name":      "doStuff"
       },
    
       doStuff : function(name) {
          console.log("doStuff called...");
       }
    });
    
    Component1Controller = Backbone.Controller.extend({
       routes: {
          "xyz123":      "doMoreStuff"
       },
    
       doMoreStuff : function() {
          console.log("doMoreStuff called...");
       }
    });
    

    so if the url is "http://mysite.com/#xyz123", then I am seeing 'doStuff()' called, or if I comment out that route, then 'doMoreStuff()' is called. But not both.

    I'm using this architecture because my page is highly component oriented, and each component defines its own Controller. A 'component manager' also defines a Controller which does some house keeping on all routes.

    Should I be able to configure two controllers that both respond to the same route? Cheers,

    Colin

  • hawkett
    hawkett about 13 years
    Thanks Elf - in this case, a page arbitrarily includes components from different developers, so I can't explicitly pre-code the controller. Only 1 component is visible at one time, with the others in the DOM, but hidden. The hash has parts: /<componentName>/*<componentState>. The 1st identifies which component to show, the rest is for the component. From what you're saying, I might try an approach where each component registers a route and callback with the main controller, which does the routes and proxies the callbacks. A custom delegate controller pattern I guess. Ta.
  • Johnny Oshika
    Johnny Oshika over 12 years
    Elf, a couple of corrections. 1) You can have as many Controllers (now called Routers) as you'd like on a page. Backbone will simply keep prepending the route definitions in the History singleton. 2) The order in which routes are checked is last entered = first checked.
  • taxilian
    taxilian over 12 years
    This is really too bad; it seems a bit shortsighted on the part of the backbone developers, since there are perfectly valid reasons why you might want to have multiple parts of a page that don't have to be tightly related to each other in order to work. Not everyone wants "one page controller to rule them all"
  • Elf Sternberg
    Elf Sternberg over 12 years
    There is nothing that stops you from having multiple parts of the page that are not tightly related. I've done it all the time in Backbone. The router does not have to be the page controller. If you want a single-page app with individual views that are bookmarkable, you use the Router.

Related