3

I've noticed that some web sites offer Ajax-ian search that refreshes the URL and displays the GET params used, for example:

someapp.com/search/Tokyo?price_min=80&price_max=300

As a result of an Ajax GET request.

I want to know how can I accomplish this by using Backbone.js, I understand that by using backbone's push state this may be possible, am I right?

How could I define a route like that (let's say the same case, scoped to /search) for a Place model for example?

Where would I do this? in a Router or in a Model?

I appreciate all the answers regarding this topic. And I apologize in advance for not providing any code, I usually do, but this exercise will be a proof of concept I'd like to make, and I hope backbone is the right tool for the job.

Thank you!

jlstr
  • 2,986
  • 6
  • 43
  • 60
  • 1
    Please, see my answer to a similar question [stackoverflow.com/questions/7445353/key-value-pair-params-handling-in-backbone-js-router/8184621#8184621](http://stackoverflow.com/questions/7445353/key-value-pair-params-handling-in-backbone-js-router/8184621#8184621) – avrelian Aug 12 '12 at 07:28
  • Thank you, looks interesting, I'll take a look at it, what do you think of this answer: http://stackoverflow.com/questions/6659283/backbone-js-fetch-with-parameters – jlstr Aug 12 '12 at 14:34
  • 1
    I think it's a better idea to start from the router. You navigate (programmatically or from the address bar) to `someapp.com/search/Tokyo?price_min=80&price_max=300`, the router parses your params, and fetches your collection with these params by `collection.fetch({ data: $.param({ price_min: 80, price_max: 300}) });` or by means of a custom `sync` method of your collection. Of course, you can do it in the opposite way, but in this case it would be hard do handle bookmarked links. – avrelian Aug 12 '12 at 17:59
  • 1st of all Thanks for all your help! I wanted to clarify that, what I'm doing is fetching a collection as a result of clicking a button, ie. @collection.fetch({data: {author: 'author'}}), This works very well, My final goal however is: to DISPLAY THE GET URL IN THE BROWSER, like, for the same example: http://myapp.com/s/posts?author=author is shown in the browser URL box, not just internally. How can I do this? – jlstr Aug 12 '12 at 19:38

1 Answers1

5

This is a working example of the solution - jsfiddle.net/avrelian/dGr8Y/, except that jsFiddle does not allow Backbone.history.navigate method to function properly.

Suppose, we have a button

<input class="fetch-button" type="button" value="Fetch" />​

and a handler

$('.fetch-button').click(function() {
  Backbone.history.navigate('posts/?author=martin', true);
});

This is our collection of posts

var Posts = Backbone.Collection.extend({
    url: 'api/posts'
});

This is our Router with a custom parameter extractor

var Router = Backbone.Router.extend({
    routes: {
        'posts/?:filters': 'filterPosts'
    },
    filterPosts: function(filters){
        posts.fetch({data: $.param(filters)});
    },
    _extractParameters: function(route, fragment) {
        var result = route.exec(fragment).slice(1);
        result.unshift(deparam(result[result.length-1]));
        return result.slice(0,-1);
    }
});

It is simplified $.deparam analog. You could use your own instead.

var deparam = function(paramString){
    var result = {};
    if( ! paramString){
        return result;
    }
    $.each(paramString.split('&'), function(index, value){
        if(value){
            var param = value.split('=');
            result[param[0]] = param[1];
        }
    });
    return result;
};

And finally, instantiation of posts collection and router object

var posts = new Posts;

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

When a user clicks on the button address bar changes to myapp.com/s/#posts?author=martin. Please, note the sign #. We use a hash query string. Of course, you can use push state, but it is not widespread yet.

avrelian
  • 798
  • 6
  • 10
  • Yeah! Looks like it's what I need, Awesome, thank you so much! Let me study it for a while. it seems the navigate method does the trick, right? – jlstr Aug 12 '12 at 22:50
  • The exercise is very clear on what it does, however, I do have one question, At which point does the '_extractParameters' method of the router gets called? (I understand that one parses the GET url params, right?) Thanks again! – jlstr Aug 12 '12 at 23:14
  • The `_bindRoutes` method of the Router is called from the Router constructor. It calls the `route` method for each route of the Router. This method calls the `Backbone.history.route` method with a special callback which should call `_extractParameters` method on invokation. This callback is adding to the `handlers` array of the `Backbone.History`. – avrelian Aug 13 '12 at 19:37
  • 1
    And then, when a user navigates to address specified in the route of the Router, or the `navigate` method is invoked programmatically (as in your example) the `loadUrl` method of the `Backbone.History` is called. In this method Backbone is choosing and calling appropriate handler, which, as you have already guessed, is that special callback with `_extractParameters` method call inside. – avrelian Aug 13 '12 at 19:44
  • Excellent! You have without a doubt helped me A LOT! Thanks, I will certainly accept your answer but, I wanted to ask you something else: Why aren't these (callback) methods shown in the backbonejs.org documentation? I'm confused. and it's not easy to find documentation about them anywhere else. – jlstr Aug 13 '12 at 19:57
  • The `_extractParameters` method is not documented, since it is not intended for public use. And I'm sorry to not mention that earlier. If your filter params do not change in number and composition, or have few variations you can, of course, get rid of `_extractParameters` method changing. In this case you should specify routes in this way `routes: {'posts/?author=:author': 'filterPostsByAuthor', 'posts/?price_min=:price_min&price_max=:price_max': 'filterPostsByPrice'}`. Please, see [updated example](http://jsfiddle.net/avrelian/dGr8Y/1/) – avrelian Aug 13 '12 at 21:18
  • Man, I'm already ashamed of myself because of the abusive number of questions I've asked you, but I must ask one final question(to be clear): If the order of the params in the URL isn't fixed or the number of params isn't constant, then which strategy should I use? the _extractParameters one? (is _extractParameters conventional to use?) THANKS ! – jlstr Aug 13 '12 at 22:46
  • Of course, it's not conventional to use `_extractParameters` method. Moreover, it's not conventional at all to use query strings in routes. Please, read [this comment](https://github.com/documentcloud/backbone/pull/668#issuecomment-2961036) of Jeremy Ashkenas, the creator of Backbone, on the topic. Another possible approach is to use `*splat` in the routes, but in this case you should parse your params manually in each router. So, I recommend to use own `_extractParameters` method for this task. – avrelian Aug 14 '12 at 06:46