Let's say I'm using Backbone pushstate and I navigate to a page with query params:
domain.com/user/111?hello=123
When I execute this:
Backbone.history.navigate('/settings', true);
My settings page loads perfectly, but the ?hello=123 stays in the URL...
domain.com/settings?hello=123
And that query param stays in the URL everywhere I navigate the site...
Backbone routing and query parameters are an unhappy marriage. Problems are well documented in this GitHub issue.
The core problem is that Backbone.Router
is designed to work with URL hash fragments as well as the pushState API. When using hash URLs the query string precedes the hash, and is never matched in the route. With pushState the query string is part of the URL fragment, and requires a different route expression.
Let's say you'd have a route search
, and that route would optionally take parameters q
, sort
and type
. As a query string that would look something like:
search?q=kittens&sort=asc&type=images
The problem is, that for users of older browsers, Backbone will revert to hashchange
based routing, and the route will become:
?q=kittens&sort=asc&type=images#search
The plugin you use tries to work around this problem, but doesn't resolve the core issue.
If possible, you should consider not using query strings, and pass any state information using optional fragments in your route expressions. The previous example routes would then become:
//pushState
search/q/kittens/sort/asc/type/images
//hash fragment
#search/q/kittens/sort/asc/type/images
Using (optional)
route parts and :captures
(docs), you could represent this URL with the following expression:
var Router = Backbone.Router.extend({
routes: {
"search(/q/:query)(/sort/:sort)(/type/:type)": "search"
},
search: function(query, sort, type) {
console.log(query, sort, type); //-> "kittens", "asc", "images"
}
});
As long as the route fragments are in the specified order, this will match urls with none, any and all parameters, for example:
search //-> undefined, undefined, undefined
search/q/kittens/type/images //-> "kittens", undefined, "images"
search/sort/asc/type/images //-> undefined, "asc", "images"
This way you don't have to worry about third-party query string libraries or browser compatibility. And if you ask me, the latter type of URL looks cleaner as well.
You don't have to use the backbone-query-parameters
plugin to deal with this anymore, just make sure that you have the latest version of Backbone and then overwrite the loadUrl
method inside History.prototype
.
// Regex to match search query strings
var searchStripper = /\?.*$/g;
// Attempt to load the current URL fragment. If a route succeeds with a
// match, returns `true`. If no defined routes matches the fragment,
// returns `false`.
loadUrl: function(fragmentOverride) {
var fragment = this.fragment = this.getFragment(fragmentOverride);
fragment = fragment.replace(searchStripper, ''); // remove the search query parameter
var matched = _.any(this.handlers, function(handler) {
if (handler.route.test(fragment)) {
handler.callback(fragment);
return true;
}
});
return matched;
},
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With