38

Will Backbone.Router.navigate set test to true:

var test = false;

var Router = Backbone.Router.extend({
  routes: {
    'posts': 'showPosts'
  },
  showPosts: function () {
    test = true;
  }
});

router = new Router();
Backbone.history.start();

router.navigate('posts?foo=3', {trigger: true});

assert.ok(test);

Eg, will posts?foo=3 fragment will match the posts route by default, or do I have to set another route for that, for example: posts?*querystring?

Thank you

PS: I know there exist the backbone-query-parameters but I want to know just for backbone.

abernier
  • 27,030
  • 20
  • 83
  • 114

5 Answers5

68

You need to add another route with that expecting parameter :

routes: {
    'posts?foo=:foo' : 'showPosts',
    'posts': 'showPosts'
},
showPosts: function (foo) {
    if(typeof foo != 'undefined'){
       // foo parameters was passed
    }
    test = true;
}

update
You could define the general route to return all the query string and then parse it in the handler :

routes: {
   'posts': 'showPosts',
   'posts?*queryString' : 'showPosts'
},
showPosts: function (queryString) {
    var params = parseQueryString(queryString);
    if(params.foo){
        // foo parameters was passed
    }
}  
...
// and the function that parses the query string can be something like : 
function parseQueryString(queryString){
    var params = {};
    if(queryString){
        _.each(
            _.map(decodeURI(queryString).split(/&/g),function(el,i){
                var aux = el.split('='), o = {};
                if(aux.length >= 1){
                    var val = undefined;
                    if(aux.length == 2)
                        val = aux[1];
                    o[aux[0]] = val;
                }
                return o;
            }),
            function(o){
                _.extend(params,o);
            }
        );
    }
    return params;
}

update 2

Here's a live demo to see the code in action.

gion_13
  • 41,171
  • 10
  • 96
  • 108
  • In one case you define the most specific route (`posts?foo=:foo`) first, and on the other case on second position: doesn't order matter? – abernier Jul 26 '12 at 14:52
  • no , it doesn't affect how routes are being applied. It's just a matter of preference. BTW, I've tested & updated the function and now it works great. – gion_13 Jul 26 '12 at 14:53
  • 1
    I see this demo working, but for reasons I still cannot fathom, this does not work for me. Using these route constructions, queryString is always undefined. – kinakuta Aug 23 '13 at 18:14
  • I see what the issue is - while you're able to execute the route by passing true to navigate, this still doesn't not work with history correctly. Additionally, having to pass true to navigate is less than ideal as it requires my router execute navigation rather than simply executing interpreted routes. – kinakuta Aug 23 '13 at 20:27
  • This answer is getting far from the initial question... Your answer is now more a how to parse a querystring... – abernier Feb 19 '14 at 09:36
  • This works only when triggering Router.navigate('posts?foo=bar',{trigger:true,replace:true}); with replace:true. Is there a way for it to work when you go directly to the url? – Mark Robson May 08 '14 at 10:59
  • Matching on the whole query string **does not work** when you are using URI encoded query parameters (e.g. "foo=Max%20%26%20Moritz") because backbone.js only expects full path fragments and already calls decodeURIComponent on every route parameters. Therefore parsing the query string later will not work correctly. – Luis Jun 22 '15 at 09:32
18

Just to complement the previous answers, instead of defining two routes that have the same callback, like:

routes: {
    'posts': 'showPosts',
    'posts?*querystring': 'showPosts'
}

You could have only one route to keep the code cleaner:

routes: {
    'posts(?*querystring)': 'showPosts'
}
romaia
  • 413
  • 3
  • 6
  • 2
    **Matching on the whole query string does not work** when you are using URI encoded query parameters (e.g. "foo=Max%20%26%20Moritz") because backbone.js only expects full path fragments and already calls decodeURIComponent on every route parameters. Therefore parsing the query string later will not work correctly. – Luis Jun 22 '15 at 09:45
6

Backbone docs:

Routes can contain parameter parts, :param, which match a single URL component between slashes; and splat parts *splat, which can match any number of URL components.

If you still want to keep the functionality without the matching you can define two routes

routes: {
  'posts': 'showPosts',
  'posts?*querystring': 'showPosts'
}

showPosts: function(querystring) {
  if (querystring) {
    // here you can parse your querystring, for your case the querystring variable is 
    // 'foo=3'
  }
  //here you'll show posts according to the querystring (or lack thereof)      
}
jakee
  • 18,486
  • 3
  • 37
  • 42
  • Is `?*` without a name for the splat `?*querystring` ok? – abernier Jul 26 '12 at 14:42
  • Is this supposed to work in Backbone 1? Cz it's not triggering any route action after I added the '?*query' – Daniel Birowsky Popeski Jun 27 '13 at 00:54
  • The answer was made almost a year ago. But the docs or changelogs don't hint towards any reasons for this not to work – jakee Jun 27 '13 at 06:52
  • 1
    I'm having the same problem. Having a route with ?*queryString not only doesn't capture the queryString variable, but if I have this and not the route without the query string, the route doesn't get interpreted at all. – kinakuta Aug 23 '13 at 04:36
5

Here's another take, still using lodash (underscore). Removed the _.map, added a bit of verbosity to the variables, and stripped out the starting '?' if present:

function parseQueryString(queryString)
{
    if (!_.isString(queryString))
        return
    queryString = queryString.substring( queryString.indexOf('?') + 1 )
    var params = {}
    var queryParts = decodeURI(queryString).split(/&/g)
    _.each(queryParts, function(val)
        {
            var parts = val.split('=')
            if (parts.length >= 1)
            {
                var val = undefined
                if (parts.length == 2)
                    val = parts[1]
                params[parts[0]] = val
            }
        })
    return params
}
blented
  • 2,699
  • 2
  • 23
  • 17
1

RFC 3986 "syntax for URIs" states that query parameters shold come before hash fragment.

In URIs a hashmark # introduces the optional fragment near the end of the URL. The generic RFC 3986 syntax for URIs also allows an optional query part introduced by a question mark ?. In URIs with a query and a fragment, the fragment follows the query.

I have this issue handling a redirect I am getting from the server i.e. "http://foo.com/main.html?error=errormessage#site". I would like to route on the query but can't see a way to write the backbone route expression to handle this url. For now I just route on the hash and check for a query by parsing location.search.