javascript 如何使 Backbone.js Collection 项目独一无二?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6416958/
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
How to make Backbone.js Collection items Unique?
提问by Keven Ruggish
Say I have these Backbone.js Model:
假设我有这些 Backbone.js 模型:
var Truck = Backbone.Model.extend({});
var truck1 = new Truck();
var truck2 = new Truck();
truck1.set("brand", "Ford");
truck2.set("brand", "Toyota");
truck3.set("brand", "Honda");
truck4.set("brand", "Ford");
Then, let's say we have a Backbone.js Collection:
然后,假设我们有一个 Backbone.js 集合:
var TruckList = Backbone.Collection.extend({
model: Truck,
comparator: function(truck) {
return truck.get("brand");
};
});
I'm a car collector, so time to add each car to my collection:
我是一名汽车收藏家,所以是时候将每辆车添加到我的收藏中了:
Trucks = new TruckList();
Trucks.add(truck1);
Trucks.add(truck2);
Trucks.add(truck3);
Trucks.add(truck4);
Just focusing on the brand attribute, truck4 is a duplicate of truck1. I can't have duplicates in my Collection. I need my collection to have unique values.
仅关注品牌属性,truck4 是 Truck1 的副本。我的收藏中不能有重复项。我需要我的收藏具有独特的价值。
My question is, How do I remove duplicate items from my Backbone.js Collection?
我的问题是,如何从 Backbone.js 集合中删除重复项?
Should I use Underscore.js for this? If so, can someone please provide a working/runnable example of how to do this.
我应该为此使用 Underscore.js 吗?如果是这样,请有人提供一个工作/可运行的示例来说明如何执行此操作。
Assume the following:
假设如下:
1.Collection is not sorted
1.集合未排序
Removal must be done on brand attribute value
Ajax call to populate each instance of a Truck. This means when adding to a collection, you don't have access to the Truck properties.
必须对品牌属性值进行移除
Ajax 调用来填充卡车的每个实例。这意味着在添加到集合时,您无权访问 Truck 属性。
回答by Peter Lyons
I would override the add
method in your TruckList collection and use underscore to detect duplicates there and reject the duplicate. Something like.
我会覆盖add
TruckList 集合中的方法并使用下划线检测那里的重复项并拒绝重复项。就像是。
TruckList.prototype.add = function(truck) {
// Using isDupe routine from @Bill Eisenhauer's answer
var isDupe = this.any(function(_truck) {
return _truck.get('brand') === truck.get('brand');
});
// Up to you either return false or throw an exception or silently ignore
// NOTE: DEFAULT functionality of adding duplicate to collection is to IGNORE and RETURN. Returning false here is unexpected. ALSO, this doesn't support the merge: true flag.
// Return result of prototype.add to ensure default functionality of .add is maintained.
return isDupe ? false : Backbone.Collection.prototype.add.call(this, truck);
}
回答by dam
The simplest way to achieve this is to make sure the models you are adding have unique ids. By default Backbone collections will not add models with duplicate ids.
实现这一目标的最简单方法是确保您添加的模型具有唯一的 ID。默认情况下,Backbone 集合不会添加具有重复 ID 的模型。
test('Collection should not add duplicate models', 1, function() {
var model1 = {
id: "1234"
};
var model2 = {
id: "1234"
};
this.collection.add([model1, model2]);
equal(1, this.collection.length, "collection length should be one when trying to add two duplicate models");
});
回答by Bill Eisenhauer
Try this. It uses the any underscore method to detect the potential duplicate and then dumps out if so. Of course, you might want to dress this up with an exception to be more robust:
试试这个。它使用任何下划线方法来检测潜在的重复项,如果是,则转储出来。当然,您可能希望将其打扮成一个例外,以使其更加健壮:
TruckList.prototype.add = function(newTruck) {
var isDupe = this.any(function(truck) {
return truck.get('brand') === newTruck.get('brand');
}
if (isDupe) return;
Backbone.Collection.prototype.add.call(this, truck);
}
As an aside, I would probably write a function on Truck to do the dupe checking so that the collection doesn't know too much about this condition.
顺便说一句,我可能会在 Truck 上写一个函数来进行重复检查,这样集合就不会对这种情况了解太多。
回答by Nathan Do
var TruckList = Backbone.Collection.extend({
model : Truck,
// Using @Peter Lyons' answer
add : function(truck) {
// Using isDupe routine from @Bill Eisenhauer's answer
var isDupe = this.any(function(_truck) {
return _truck.get('brand') === truck.get('brand');
});
if (isDupe) {
// Up to you either return false or throw an exception or silently
// ignore
return false;
}
Backbone.Collection.prototype.add.call(this, truck);
},
comparator : function(truck) {
return truck.get("brand");
} });
VassilisB's answer worked great but it will override Backbone Collection's add() behavior. Therefore, errors might come when you try to do this:
VassilisB 的回答效果很好,但它会覆盖 Backbone Collection 的 add() 行为。因此,当您尝试执行此操作时可能会出现错误:
var truckList = new TruckList([{brand: 'Ford'}, {brand: 'Toyota'}]);
So, I added a bit of a checking to avoid these errors:
因此,我添加了一些检查以避免这些错误:
var TruckList = Backbone.Collection.extend({
model : Truck,
// Using @Peter Lyons' answer
add : function(trucks) {
// For array
trucks = _.isArray(trucks) ? trucks.slice() : [trucks]; //From backbone code itself
for (i = 0, length = trucks.length; i < length; i++) {
var truck = ((trucks[i] instanceof this.model) ? trucks[i] : new this.model(trucks[i] )); // Create a model if it's a JS object
// Using isDupe routine from @Bill Eisenhauer's answer
var isDupe = this.any(function(_truck) {
return _truck.get('brand') === truck.get('brand');
});
if (isDupe) {
// Up to you either return false or throw an exception or silently
// ignore
return false;
}
Backbone.Collection.prototype.add.call(this, truck);
}
},
comparator : function(truck) {
return truck.get("brand");
}});
回答by George R
I'm doing a FileUpload thing with the same issue, and here's how I did it (coffeescript):
我正在用同样的问题做一个 FileUpload 事情,这是我是怎么做的(coffeescript):
File = Backbone.Model.extend
validate: (args) ->
result
if [email protected](args)
result = 'File already in list'
result
Files = Backbone.Collection.extend
model: File
isUniqueFile: (file) ->
found
for f in @models
if f.get('name') is file.name
found = f
break
if found
false
else
true
... and that's it. The collection object is automatically referenced in File, and Validation is automatically called when an action is invoked on the collection which in this case is Add.
......就是这样。集合对象在文件中自动引用,并且在集合上调用操作时自动调用验证,在本例中为添加。
回答by Teddy
Underscore.js, a pre-req for backbone.js, provides a function for this: http://documentcloud.github.com/underscore/#uniq
Underscore.js 是backbone.js 的先决条件,为此提供了一个函数:http: //documentcloud.github.com/underscore/#uniq
Example:
例子:
_.uniq([1,1,1,1,1,2,3,4,5]); // returns [1,2,3,4,5]
回答by Vassilis Blazos
I would prefer override the add method like this.
我更喜欢像这样覆盖 add 方法。
var TruckList = Backbone.Collection.extend({
model : Truck,
// Using @Peter Lyons' answer
add : function(truck) {
// Using isDupe routine from @Bill Eisenhauer's answer
var isDupe = this.any(function(_truck) {
return _truck.get('brand') === truck.get('brand');
});
if (isDupe) {
// Up to you either return false or throw an exception or silently
// ignore
return false;
}
Backbone.Collection.prototype.add.call(this, truck);
},
comparator : function(truck) {
return truck.get("brand");
} });
回答by jlleblanc
Not sure if this is an update to either Backbone or underscore, but the where()
function works in Backbone 0.9.2 to do the matching for you:
不确定这是对 Backbone 还是下划线的更新,但该where()
函数在 Backbone 0.9.2 中可以为您进行匹配:
TruckList.prototype.add = function(truck) {
var matches = this.where({name: truck.get('brand')});
if (matches.length > 0) {
//Up to you either return false or throw an exception or silently ignore
return false;
}
Backbone.Collection.prototype.add.call(this, truck);
}
回答by Baer
It seems like an elegant solution would be to use _.findWhere so long as you have some unique attribute (brand in your case). _.findWhere will return a match which is a JavaScript object and therefore truthy or undefined which is falsey. This way you can use a single if statement.
似乎一个优雅的解决方案是使用 _.findWhere 只要您有一些独特的属性(在您的情况下是品牌)。_.findWhere 将返回一个匹配项,它是一个 JavaScript 对象,因此 true 或 undefined 是 false。这样您就可以使用单个 if 语句。
var TruckList = Backbone.Collection.extend({
model: Truck,
add: function (truck) {
if (!this.findWhere({ brand: truck.get('brand') })) {
Backbone.Collection.prototype.add.call(this, truck);
}
}
});
回答by Sean Anderson
I'm really unhappy with the accepted answer to this solution. It contains numerous errors. I've edited the original solution to highlight my concerns, but I am proposing the following solution assuming you're OK dirtying your duplicate's id/cid property:
我对这个解决方案的公认答案非常不满意。它包含许多错误。我已经编辑了原始解决方案以突出我的担忧,但我提出以下解决方案,假设您可以弄脏重复的 id/cid 属性:
TruckList.prototype.add = function(truckToAdd, options) {
// Find duplicate truck by brand:
var duplicateTruck = this.find(function(truck){
return truck.get('brand') === truckToAdd.get('brand');
});
// Make truck an actual duplicate by ID:
// TODO: This modifies truckToAdd's ID. This could be expanded to preserve the ID while also taking into consideration any merge: true options.
if(duplicateTruck !== undefined){
if(duplicateTruck.has('id')){
truckToAdd.set('id', duplicateTruck.get('id'), { silent: true });
}
else {
truckToAdd.cid = duplicateTruck.cid;
}
}
// Allow Backbone to handle the duplicate instead of trying to do it manually.
return Backbone.Collection.prototype.add.call(this, truckToAdd, options);
}
The only flaw with this one is that truckToAdd's ID/cid is not preserved. However, this does preserve all of the expected functionality of adding an item to a collection including passing merge: true.
这个唯一的缺陷是没有保留 TruckToAdd 的 ID/cid。但是,这确实保留了将项目添加到集合的所有预期功能,包括传递合并:true。