I'd like to implement a search page using Backbone.js
. The search parameters are taken from a simple form, and the server knows to parse the query parameters and return a json array of the results. My model looks like this, more or less:
App.Models.SearchResult = Backbone.Model.extend({
urlRoot: '/search'
});
App.Collections.SearchResults = Backbone.Collection.extend({
model: App.Models.SearchResult
});
var results = new App.Collections.SearchResults();
I'd like that every time I perform results.fetch()
, the contents of the search form will also be serialized with the GET
request. Is there a simple way to add this, or am I doing it the wrong way and should probably be handcoding the request and creating the collection from the returned results:
$.getJSON('/search', { /* search params */ }, function(resp){
// resp is a list of JSON data [ { id: .., name: .. }, { id: .., name: .. }, .... ]
var results = new App.Collections.SearchResults(resp);
// update views, etc.
});
Thoughts?
Backbone.js fetch with parameters answers most of your questions, but I put some here as well.
Add the data
parameter to your fetch
call, example:
var search_params = {
'key1': 'value1',
'key2': 'value2',
'key3': 'value3',
...
'keyN': 'valueN',
};
App.Collections.SearchResults.fetch({data: $.param(search_params)});
Now your call url has added parameters which you can parse on the server side.
Attention: code simplified and not tested
I think you should split the functionality:
The Search Model
It is a proper resource in your server side. The only action allowed is CREATE
.
var Search = Backbone.Model.extend({
url: "/search",
initialize: function(){
this.results = new Results( this.get( "results" ) );
this.trigger( "search:ready", this );
}
});
The Results Collection
It is just in charge of collecting the list of Result models
var Results = Backbone.Collection.extend({
model: Result
});
The Search Form
You see that this View is making the intelligent job, listening to the form.submit
, creating a new Search
object and sending it to the server to be created
. This created
mission doesn't mean the Search has to be stored in database, this is the normal creation
behavior, but it does not always need to be this way. In our case create
a Search means to search the DB looking for the concrete registers.
var SearchView = Backbone.View.extend({
events: {
"submit form" : "createSearch"
},
createSearch: function(){
// You can use things like this
// http://stackoverflow.com/questions/1184624/convert-form-data-to-js-object-with-jquery
// to authomat this process
var search = new Search({
field_1: this.$el.find( "input.field_1" ).val(),
field_2: this.$el.find( "input.field_2" ).val(),
});
// You can listen to the "search:ready" event
search.on( "search:ready", this.renderResults, this )
// this is when a POST request is sent to the server
// to the URL `/search` with all the search information packaged
search.save();
},
renderResults: function( search ){
// use search.results to render the results on your own way
}
});
I think this kind of solution is very clean, elegant, intuitive and very extensible.
Found a very simple solution - override the url()
function in the collection:
App.Collections.SearchResults = Backbone.Collection.extend({
urlRoot: '/search',
url: function() {
// send the url along with the serialized query params
return this.urlRoot + "?" + $("#search-form").formSerialize();
}
});
Hopefully this doesn't horrify anyone who has a bit more Backbone / Javascript skills than myself.
It seems the current version of Backbone (or maybe jQuery) automatically stringifies the data
value, so there is no need to call $.param
anymore.
The following lines produce the same result:
collection.fetch({data: {filter:'abc', page:1}});
collection.fetch({data: $.param({filter:'abc', page:1})});
The querystring will be filter=abc&page=1
.
EDIT: This should have been a comment, rather than answer.