navigate route with querystring

43,140

Solution 1

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.

Solution 2

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'
}

Solution 3

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)      
}

Solution 4

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
}

Solution 5

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.

Share:
43,140
abernier
Author by

abernier

Hi, I'm Antoine Bernier — JS fullstack webdev with 15y of experience. I love animation, unix, design and opensource. Feel free to contact me 👋 http://be.net/abernier

Updated on February 14, 2020

Comments

  • abernier
    abernier about 4 years

    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
    abernier almost 12 years
    Is ?* without a name for the splat ?*querystring ok?
  • abernier
    abernier almost 12 years
    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?
  • gion_13
    gion_13 almost 12 years
    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.
  • Daniel Birowsky Popeski
    Daniel Birowsky Popeski almost 11 years
    Is this supposed to work in Backbone 1? Cz it's not triggering any route action after I added the '?*query'
  • jakee
    jakee almost 11 years
    The answer was made almost a year ago. But the docs or changelogs don't hint towards any reasons for this not to work
  • kinakuta
    kinakuta over 10 years
    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
    kinakuta over 10 years
    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
    kinakuta over 10 years
    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.
  • abernier
    abernier about 10 years
    This answer is getting far from the initial question... Your answer is now more a how to parse a querystring...
  • Mark Robson
    Mark Robson almost 10 years
    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?
  • azz0r
    azz0r over 9 years
    Thanks for this, should have got more attention imo
  • Luis
    Luis almost 9 years
    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
    Luis almost 9 years
    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.