javascript 使用 Backbone.js 轮询集合

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5963324/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 18:57:50  来源:igfitidea点击:

Polling a Collection with Backbone.js

javascriptajaxpollingbackbone.js

提问by sneeu

I'm trying to keep a Backbone.js Collection up-to-date with what's happening on the server.

我正在尝试使 Backbone.js 集合与服务器上发生的事情保持同步。

My code is similar to the following:

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

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);

What happens is that when the comments are fetched, refreshis called, the same comments to the bottom of the CommentListView—which is what I'd expect from the code above.

所发生的情况是,当获取评论时,refresh调用底部的相同评论CommentListView——这正是我对上面代码所期望的。

What I'd like to know is what's the best way to “refresh” the view, without loosing any “local state”.

我想知道的是,在不丢失任何“本地状态”的情况下,“刷新”视图的最佳方式是什么。

采纳答案by Julien

What you want to do is refresh the collection every few seconds and append the new comments. My suggestion is to deal with that problem on your backend. Send over the last timestamp from your last comment and ask the server for the delta from this date only.

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

To do so, in your collection:

为此,请在您的收藏中:

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

In your backend, query your database based on the from_time parameter.Your client code does not change to refresh the view.

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

If you do not want to change your backend code for any reason add this line in the addAll function:

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

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

回答by Jason Stonebraker

Or just use the far simpler addition to backbone's fetch method:

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

this.fetch({ update: true });

When the model data returns from the server, the collection will be (efficiently) reset, unless you pass {update: true}, in which case it will use update to (intelligently) merge the fetched models. - Backbone Documentation

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

:-)

:-)

回答by Jason Stonebraker

Backbone.Collection.merge([options])

Backbone.Collection.merge([选项])

Building on @Jeb's response above, I've encapsulated this behavior into a Backbone extension that you can copy and paste into a .js file and include in your page (after including the Backbone library itself).

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

It provides a method called mergefor Backbone.Collection objects. Rather than fully resetting the existing collection (as fetchdoes), it compares the server response to the existing collection and merges their differences.

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

  1. It addsmodels that are in the response, but not in the existing collection.
  2. It removesmodels that are in the existing collection, but not in the response.
  3. Finally, it updatesthe attributes of models found in the existing collection AND in the response.
  1. 添加了响应中的模型,但不在现有集合中。
  2. 删除现有集合中但不在响应中的模型。
  3. 最后,它更新在现有集合和响应中找到的模型的属性。

All expected events are triggered for adding, removing, and updating models.

添加、删除和更新模型时会触发所有预期事件。

The optionshash takes successand errorcallbacks which will be passed (collection, response)as arguments, and it provides a third callback option called completethat is executed regardless of success or error (mostly helpful for polling scenarios).

选项散列接受successerror将要传递回调(collection, response)作为参数,它提供了一个名为第三回调选项complete是,无论成功或错误(大多为轮询场景有用)执行。

It triggers events called "merge:success" and "merge:error".

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

Here is the extension:

这是扩展名:

// 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; });
};

Simple Usage Scenario:

简单使用场景:

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

Error Handling Usage Scenario:

错误处理使用场景:

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

var collection = new MyCollection();

var jqXHR = collection.merge({
    success: function(model, response) {
        console.log("Merge succeeded...");
    },
    error: function(model, response) {
        console.log("Merge failed...");
        handleError(response);
    },
    complete: function() {
        console.log("Merge attempt complete...");
    }
});

function handleError(jqXHR) {
    console.log(jqXHR.statusText);

    // Direct the user to the login page if the session expires
    if(jqXHR.statusText == 'Unauthorized') {
        window.location.href = "/login";                        
    }
};

回答by Jeb

Make a duplicate collection. Fetch() it. Compare the two to find the deltas. Apply them.

制作一个重复的集合。取()它。比较两者以找到增量。应用它们。

      /*
       * 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; });
      },