Reputation: 15434
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?
Upvotes: 6
Views: 5692
Reputation: 434665
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.
Upvotes: 2