6

I am trying to redirect route to another before route callback is executed. I have following piece of code:

console.log("file loaded");
(function (History) {
    var _navigate = History.prototype.navigate;

    _.extend(History.prototype, {
        navigate: function (fragment, opts) {
            alert("adad");
            return _navigate.call(this, fragment, opts);
        }
    });
})(Backbone.History);

This code wraps Backbone.History.navigate method, and showing alert on the method call. But this alert never shows up when I am changing routes.

The console.log line is just to be sure that file has been loaded after backbone.js.

What is wrong with this code?

Drejc
  • 14,196
  • 16
  • 71
  • 106
user606521
  • 14,486
  • 30
  • 113
  • 204
  • 1
    I assume you've called Backbone.history.start() somewhere? – WiredPrairie Feb 12 '13 at 20:36
  • yes I did - my views are changing, I have no idea why this doesnt work – user606521 Feb 12 '13 at 20:42
  • It must be something else. I just plugged your code into a Backbone project I've got open, and I was instantly annoyed by the `adad` alert as soon as I navigated. :) I've got the boilerplate code anchor tag to "Navigate" code plugged into my app, which may change the behavior. – WiredPrairie Feb 12 '13 at 20:51
  • Backbone 0.9.10. Browsers = IE9 and Chrome current both work. – WiredPrairie Feb 12 '13 at 21:46

1 Answers1

2

I think you're overriding the wrong thing: that navigate isn't used the way you think it is.

Let us look at part of Backbone.history.start:

// Start the hash change handling, returning `true` if the current URL matches
// an existing route, and `false` otherwise.
start: function(options) {
  //...
  if (this._hasPushState) {
    Backbone.$(window).on('popstate', this.checkUrl);
  } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
    Backbone.$(window).on('hashchange', this.checkUrl);
  } else if (this._wantsHashChange) {
    this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
  }

You'll see that all the various ways of handling routing in Backbone go through checkUrl rather than navigate. The checkUrl method does a bit of busy work and calls loadUrl; one part of the busy work is this:

  if (this.iframe) this.navigate(current);

so navigate will be called but only when an <iframe> is being used to simulate hashchange and popstate events and AFAIK that only happens when you're using an older version of IE.

Back to the usual path through the code. We've seen that checkUrl does some busy work and calls loadUrl so what does that do? loadUrl does this:

loadUrl: function(fragmentOverride) {
  var fragment = this.fragment = this.getFragment(fragmentOverride);
  var matched = _.any(this.handlers, function(handler) {
    if (handler.route.test(fragment)) {
      handler.callback(fragment);
      return true;
    }
  });
  return matched;
}

If you look at the route method in History and the route method in Route you'll see that handler.callback is what calls the route handler from one of your routers and triggers the routing event.

The navigate method that you're replacing is pretty much only used by Router's navigate:

navigate: function(fragment, options) {
  Backbone.history.navigate(fragment, options);
  return this;
}

If you want to redirect before the route handler is called, you could replace loadUrl with something like this:

(function(History) {
    var _loadUrl = History.prototype.loadUrl;
    _.extend(History.prototype, {
        loadUrl: function() {
            var args = [].slice.apply(arguments);
            args[0] = this.getFragment(args[0]);
            // If args[0] is the fragment that you want to
            // redirect then replace it here or do whatever
            // needs to be done.
            return _loadUrl.apply(this, args);
        }
    });
})(Backbone.History);

Demo: http://jsfiddle.net/ambiguous/e4KYK/

Overall I think you'd be better off handling the redirect in a normal route handler: when the offending route is called, just call navigate on whatever router is handy.


Keep in mind that Backbone.History isn't documented beyond the start method so everything here is subject to change.

mu is too short
  • 426,620
  • 70
  • 833
  • 800