Let\'s say you are building a Twitter clone with Backbone.js. You have a collection of tweets. Each tweet is obviously an instance of the Tweet model.
You create an inst
Lets assume that every one of your tweets has a unique identifier(if not, you should probably create one).
You can structure your backend in such away that by default it gets you 10 latest tweets if you call http://your.site.com/tweets without any arguments.
If you however call http://your.site.com/tweets?last_tweet_id=BLAblaBlA, it will give you 10 latest tweets that came after the last_tweet_id that you specified.
You can override the code that gets the data from the backend into your Collection by implementing YourCollection.sync method.
Reason: Backbone.Collection first tries to call Collection.sync and if its not implemented, it calls Backbone.sync function, so if you implement YourCollection.sync, it will be used. Here is the snippet from Backbone.Collection.fetch function:
(this.sync || Backbone.sync)('read', this, success, error);
so your sync would be something like
var TweetCollection = Backbone.Collection.extend({
model: TweetModel,
sync: function(method, collection, success, error) {
var requestData={};
if(collection.length>0) {
requestData.last_tweet_id=collection.last.id
}
var params = {
url: "/tweet",
type: "POST",
data: requestData,
success: success,
error: error
};
$.ajax(params);
}
}
You would have to override your collections parse function to make sure that the response is appended to the existing array of models.
Currently, out of the box, no.
The technique I use is the following. You have your TweetCollection that extend Backbone.Collection and it has a classic REST url like "/tweets" . Your backend is sending only the 10 latest record (or page 1). To get page 2 you would use a url like "/tweets?page=2" and the backend would send the next 10 tweets. The question is really: how do you keep old record while fetching new one?
I use 2 collections:
TweetPageCollection extends TweetCollection with the following url:
page: 1,
url : function(){ return "/tweets?page=" + this.page; }
Now in your controller code you can do something like this:
initialize: function(){
this.tweets = new TweetCollection();
this.pages = new TweetPageCollection();
this.pages.bind("refresh", this.newPageReceived);
this.pages.page = 1;
this.pages.fetch();
this.tweet_list_view = new TweetListView({model: this.tweets});
},
getNextPage: function(){
this.pages.page++;
this.pages.fetch();
},
newPageReceived : function(page){
// here you can check that the tweet is not already in the tweets collections
this.tweets.add(page.toArray());
}
...
Now when you want to fetch new tweets, you call getNextPage() and when the XHR will succeed your view will be refreshed.
Same principles can be applied to refetch the latest tweets. 2 collections, 1 fetching, the other gathering fetches.
I'm not sure if this was possible back in March as I only recently started using backbone. But a better solution may be to pass standard jQuery options into Collection.fetch.
this.collection.fetch({data: {last_tweet: last_teet_id}});
Check out the jQuery documentation for a full list of parameters.
I believe this is what you were looking for:
yourCollection.fetch({add: true})
Then, you can bind your collection view's render to the add event:
yourCollectionView.bind('add', this.render, this);
Though, if the render is heavy, you'll want to delay it with a timeout to avoid immediately calling it for every addition to the collection:
yourCollectionView.bind('add', this.delayedRender, this);
delayedRender: function() {
var self = this;
if (self.renderTimer) { clearTimeout(self.renderTimer); }
self.renderTimer = setTimeout(function() {
self.render();
self.renderTimer = null;
}, 300);
}
I don't think this is a backbone issue. Using Twitter's RESTful API you can use the optional since_id parameter. After each call your collection could save the id of the most recent tweet it has fetched. Use this id in your url function and you'll only receive newer tweets.