How to filter a Backbone collection on the server
Is there a common pattern for using a Backbone service that filters a collection on the server? I haven't been able to find anything on Google and Stack Overflow search results, which is surprising given the number of basic apps in production.
Suppose I am building a new frontend using Backbone.
On the search screen, I need to pass the following information to the server and return the results that the page is facing.
- filter criteria
- sorting criteria
- results per page
- page number
The backbone doesn't seem to be very interested in filtering the upload to the server. It expects the server to return the entire list of questions and perform client-side filtering.
My guess is that in order to make this work I need to subclass Collection and override fetch so that instead of always fetching data from the same RESTful url, it passes the above parameters.
I don't want to reinvent the wheel. Am I missing a feature in Backbone that makes this process simpler or easier with existing components? Is there already a well-established pattern for solving this problem?
source to share
If you just want to pass the GET parameters in the request, you just have to specify them in the fetch call itself.
collection.fetch( {
data: {
sortDir: "ASC",
totalResults: 100
}
} );
The parameters passed to fetch should translate directly to the jQuery.ajax call and the data property should be automatically parsed. Of course, overriding the fetch method is fine too, especially if you want to standardize parts of the logic.
source to share
You are correct, creating your own Collection
is the way to go as there are no pagination standards other than OData.
Rather than overriding "fetch", what I usually do in these cases is create the property collection.url
as a function, return the correct URL based on the state of the collection.
However, in order to do the pagination, the server needs to return the total number of elements to you so you can calculate how many pages are based on the X elements on the page. Currently, some APIs use things like HAL or HATEOAS, which are basically HTTP response headers. To get this information, I usually add a listener to the event sync
that fires after any AJAX operation. If you need to notify external components (usually a view) of the number of available items / pages, use an event.
A simple example: your server returns X-ItemTotalCount
in response headers and expects the parameters page
and to be executed in the request items
.
var PagedCollection = Backbone.Collection.extend({
initialize: function(models,options){
this.listenTo(this, "sync", this._parseHeaders);
this.currentPage = 0;
this.pageSize = 10;
this.itemCount = 0;
},
url: function() {
return this.baseUrl + "?page=" + this.currentPage + "&items=" + this.pageSize;
},
_parseHeaders: function(collection,response){
var totalItems = response.xhr.getResponseHeader("X-ItemTotalCount");
if(totalItems){
this.itemCount = parseInt(totalItems);
//trigger an event with arguments (collection, totalItems)
this.trigger("pages:itemcount", this, this.itemCount);
}
}
});
var PostCollection = PagedCollection.extend({
baseUrl: "/posts"
});
Note that we are using a different property of our own baseUrl
to make it easier to extend PagedCollection. If you need to add your own initialize
, call a parent prototype like this, or you won't be parsing headers:
PagedCollection.protoype.initialize.apply(this,arguments)
You can even add methods fetchNext
and fetchPrevious
to the collection, where you simply change this.currentPage
and fetch
. Remember to add {reset:true}
as fetch
parameters if you want to replace one page with another instead of adding.
Now, if your backend for the project is consistent, any resource that allows pagination to the server can be exposed using a single PagedCollection based collection on the client, provided the same options / responses are used.
source to share