我有一个基于模型属性自行排序的集合,有时我想添加一个模型,只计算add
事件处理程序中的顺序。问题是当我在add事件处理程序中调用collection.sort()时,由于某种原因它会再次触发add事件。
由于我的add事件会执行一些DOM插入,因此我最终在DOM中获取了重复的项目。
到目前为止,我找到的唯一解决方案是在添加模型或调用_.defer并对集合进行排序然后运行任何其他代码之前计算下一个顺序
请参阅完整的小提琴:http://jsfiddle.net/DD23n/9/
答案 0 :(得分:4)
sntran关于问题的来源是正确的:你在迭代时修改数组。
您可以使用nOrder: null
at the beginning of the model list:
splice.apply(this.models, [index, 0].concat(models));
然后add
loops through the models triggering 'add'
events:
for (i = 0, length = this.models.length; i < length; i++) {
if (!cids[(model = this.models[i]).cid]) continue;
options.index = i;
model.trigger('add', model, this, options);
}
但在你的'add'
回调中,你修改了模型:
if(model.get('nOrder') == null)
model.set('nOrder', _.max(collection.pluck('nOrder')) + 1);
然后对集合进行排序:
collection.sort({silent: true});
这两个操作在事件触发循环中将this.models[0]
移动到this.models[3]
;但是,该循环的i
将继续保持一致,而新的this.models[3]
(曾经是0
)将再次超过if (!cids[(model = this.models[i]).cid])
测试,并且您的第二个'add'
事件。
你可以观看这个版本的小提琴作品,看看集合的阵列是如何在你背后改变的:
我认为最简单的解决方案是在集合中添加append
方法,在模型上设置适当的nOrder
值,然后将其添加到集合中:
append: function(m) {
var nOrder = _.max(this.pluck('nOrder')) + 1;
if(m instanceof Backbone.Model)
m.set({ nOrder: nOrder });
else
m.nOrder = nOrder;
this.add(m);
}
然后,您的'add'
回调可以单独留下nOrder
并停止对该集合进行排序。
演示:http://jsfiddle.net/ambiguous/JqWVP/
您也可以覆盖集合的add
方法,但如果您想要做得更好,那就更复杂了。
答案 1 :(得分:2)
如果您阅读了骨干的源代码,add
函数会将插入模型连接到collection.models
。在这种情况下,你有四个。
因为您没有告诉add
函数是静默的,所以它会循环遍历collection.models并触发插入元素的'add'
事件。
但是,您在sort
处理程序中调用add
,collection.models
会变得不同。当集合在add
函数中循环时,插入模型将处于不同的位置,它将再次触发“添加”。
我不确定它是否有意义,但我认为在循环期间不重新排列/修改数组会更好。