我正在努力使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
底部的相同评论 - 这是我对上述代码的期望。
我想知道的是,“刷新”视图的最佳方式是什么,而不会失去任何“本地状态”。
答案 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
那样),而是将服务器响应与现有集合进行比较并合并它们的差异。
触发所有预期事件以添加,删除和更新模型。
选项哈希需要success
和error
个回调,它们将作为参数传递(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; });
},