Backbone.js updating of models in a collection

2019-03-08 04:17发布

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 instance of the collection, fetch the latest 10 tweets, render them and add to the DOM.

So far so good.

What if you wanted to make a call to the server a few minutes later to see if any new tweets arrived? How can you add the newly arrived tweets to the collection?

If you use the fetch() method, you are hitting the same URL all the time. That's fine. Is there a clever way that I can use Backbone/Underscore to filter those and add the tweets that aren't in the collection to the collection?

5条回答
Rolldiameter
2楼-- · 2019-03-08 04:57

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.

查看更多
成全新的幸福
3楼-- · 2019-03-08 05:03

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);
}
查看更多
萌系小妹纸
4楼-- · 2019-03-08 05:17

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:

  • The first one is the one used to render the tweets on the screen. It is a normal TweetCollection.
  • The second one is the one used to get the pages from the server. Let's call it TweetPageCollection.

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.

查看更多
Fickle 薄情
5楼-- · 2019-03-08 05:20

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.

查看更多
可以哭但决不认输i
6楼-- · 2019-03-08 05:24

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.

查看更多
登录 后发表回答