使用Backbone.js轮询集合

时间:2011-05-11 11:16:50

标签: javascript ajax polling backbone.js

我正在努力使Backbone.js Collection与服务器上发生的事情保持同步。

我的代码类似于以下内容:

var Comment = Backbone.Model.extend({});
var CommentCollection = Backbone.Collection.extend({
    model: Comment
});

var CommentView = Backbone.View.extend({ /* ... */ });
var CommentListView = Backbone.View.extend({
    initialize: function () {
        _.bindAll(this, 'addOne', 'addAll');

        this.collection.bind('add', this.addOne);
        this.collection.bind('refresh', this.addAll);
    },
    addOne: function (item) {
        var view = new CommentView({model: item});
        $(this.el).append(view.render().el);
    },
    addAll: function () {
        this.collection.each(this.addOne);
    }
});

var comments = new CommentCollection;
setInterval(function () {
    comments.fetch();
}, 5000);

当提取评论时,会调用refresh,对CommentListView底部的相同评论 - 这是我对上述代码的期望。

我想知道的是,“刷新”视图的最佳方式是什么,而不会失去任何“本地状态”。

4 个答案:

答案 0 :(得分:30)

或者只是使用更为简单的添加到主干的获取方法:

this.fetch({ update: true });
  

当模型数据从服务器返回时,集合将被(有效地)重置,除非您传递{update:true},在这种情况下,它将使用update(智能地)合并获取的模型。 - Backbone Documentation

: - )

答案 1 :(得分:16)

您要做的是每隔几秒刷新一次这个集合并附加新的评论。我的建议是在你的后端处理这个问题。从上次评论中发送最后一个时间戳,并仅在此日期向服务器询问增量。

为此,请在您的收藏中:

CommentCollection = Backbone.Collection.extend({
  url: function(){
    return "/comments?from_time=" + this.last().get("created_at");
  },
  comparator: function(comment){
    return comment.get("created_at");
  }
});

在后端,根据from_time参数查询数据库。您的客户端代码不会更改以刷新视图。

如果您因任何原因不想更改后端代码,请在addAll函数中添加以下行:

addAll: function(){
  $(this.el).empty();
  this.collection.each(this.addOne);
} 

答案 2 :(得分:8)

<强> Backbone.Collection.merge([选项])

基于@ Jeb上面的回复,我将此行为封装到Backbone扩展中,您可以将其复制并粘贴到.js文件中并包含在您的页面中(包括Backbone库本身之后)。

它为Backbone.Collection对象提供了一个名为merge的方法。它不是完全重置现有集合(如fetch那样),而是将服务器响应与现有集合进行比较并合并它们的差异。

  1. 添加响应中的模型,但不在现有集合中。
  2. 删除现有集合中的模型,但不会删除响应中的模型。
  3. 最后,它更新在现有集合和响应中找到的模型的属性。
  4. 触发所有预期事件以添加,删除和更新模型。

    选项哈希需要successerror个回调,它们将作为参数传递(collection, response),并提供第三个回调选项{{1}无论成功或错误都会执行(主要对轮询方案有用)。

    它会触发名为“merge:success”和“merge:error”的事件。

    以下是扩展程序:

    complete

    简单使用场景:

    // Backbone Collection Extensions
    // ---------------
    
    // Extend the Collection type with a "merge" method to update a collection 
    // of models without doing a full reset.
    
    Backbone.Collection.prototype.merge = function(callbacks) {
        // Make a new collection of the type of the parameter 
        // collection.
        var me = this;
        var newCollection = new me.constructor(me.models, me.options);
        this.success = function() { };
        this.error = function() { };
        this.complete = function() { };
    
        // Set up any callbacks that were provided
        if(callbacks != undefined) {
            if(callbacks.success != undefined) {
                me.success = callbacks.success;
            }
    
            if(callbacks.error != undefined) {
                me.error =  callbacks.error;
            }
    
            if(callbacks.complete != undefined) {
                me.complete = callbacks.complete;
            }
        }
    
        // Assign it the model and url of collection.
        newCollection.url = me.url;
        newCollection.model = me.model;
    
        // Call fetch on the new collection.
        return newCollection.fetch({
            success: function(model, response) {
                // Calc the deltas between the new and original collections.
                var modelIds = me.getIdsOfModels(me.models);
                var newModelIds = me.getIdsOfModels(newCollection.models);
    
                // If an activity is found in the new collection that isn't in
                // the existing one, then add it to the existing collection.
                _(newCollection.models).each(function(activity) {
                    if (_.indexOf(modelIds, activity.id) == -1) { 
                        me.add(activity);
                    }
                }, me);
    
                // If an activity in the existing collection isn't found in the
                // new one, remove it from the existing collection.
                var modelsToBeRemoved = new Array();
                _(me.models).each(function(activity) {
                    if (_.indexOf(newModelIds, activity.id) == -1) {  
                        modelsToBeRemoved.push(activity);
                    }
                }, me);
                if(modelsToBeRemoved.length > 0) {
                    for(var i in modelsToBeRemoved) {
                        me.remove(modelsToBeRemoved[i]);
                    }
                }
    
                // If an activity in the existing collection is found in the
                // new one, update the existing collection.
                _(me.models).each(function(activity) {
                    if (_.indexOf(newModelIds, activity.id) != -1) { 
                        activity.set(newCollection.get(activity.id));  
                    }
                }, me);
    
                me.trigger("merge:success");
    
                me.success(model, response);
                me.complete();
            },
            error: function(model, response) {
                me.trigger("merge:error");
    
                me.error(model, response);
                me.complete();
            }
        });
    };
    
    Backbone.Collection.prototype.getIdsOfModels = function(models) {
            return _(models).map(function(model) { return model.id; });
    };
    

    错误处理使用场景:

    var MyCollection = Backbone.Collection.extend({
      ...
    });
    var collection = new MyCollection();
    collection.merge();
    

答案 3 :(得分:3)

制作重复的收藏品。获取()它。比较两者以找到增量。应用它们。

      /*
       * Update a collection using the changes from previous fetch,
       * but without actually performing a fetch on the target 
       * collection. 
       */
      updateUsingDeltas: function(collection) {
        // Make a new collection of the type of the parameter 
        // collection.
        var newCollection = new collection.constructor(); 

        // Assign it the model and url of collection.
        newCollection.url = collection.url;
        newCollection.model = collection.model;

        // Call fetch on the new collection.
        var that = this;
        newCollection.fetch({
          success: function() {
            // Calc the deltas between the new and original collections.
            var modelIds = that.getIdsOfModels(collection.models);
            var newModelIds = that.getIdsOfModels(newCollection.models);

            // If an activity is found in the new collection that isn't in
            // the existing one, then add it to the existing collection.
            _(newCollection.models).each(function(activity) {
              if (modelIds.indexOf(activity.id) == -1) { 
                collection.add(activity);
              }
            }, that);

            // If an activity in the existing colleciton isn't found in the
            // new one, remove it from the existing collection.
            _(collection.models).each(function(activity) {
              if (newModelIds.indexOf(activity.id) == -1) {  
                collection.remove(activity);  
              }
            }, that);

            // TODO compare the models that are found in both collections,
            // but have changed. Maybe just jsonify them and string or md5
            // compare.
          }
        });
      },

      getIdsOfModels: function(models) {
        return _(models).map(function(model) { return model.id; });
      },