问题
I have a BackboneJS collection called History which can contain one of several Backbone JS models (which extend from HistoryItem which extends from Backbone.Model), I trying to find a way to have this recreated when loading, unfortunately it seems BackboneJS collection can only specify at particular model e.g.
HistoryCollection = Backbone.Model.extend({
model: app.models.HistoryItem
})
What I really need to do is determine this per type, here is what I'd like to do
HistoryCollection = Backbone.Model.extend({
model: function(item) {
return app.models[item.type]; } })
Any ideas before I fork Backbone to implement this? (i.e. a collection model attribute being able to take a function)
回答1:
Playing around in firebug.. came up with an approach where you can override the parse method of collection instead of specifying the model. Your parse implementation basically becomes a simple factory for filling the collection with models that you want:
var BaseModel = Backbone.Model.extend({
meth: function(){ return 'Base method'; },
});
var SubModel = BaseModel.extend({
meth: function(){ return 'Sub1 method'; }
});
var SubModel2 = BaseModel.extend({
meth: function(){ return 'Sub2 method'; }
});
var ModelCollection = Backbone.Collection.extend({
parse: function(data){
var self = this;
data.forEach(function(item){
switch(item.type){
case 1:
self.add(new SubModel(data));
break;
case 2:
self.add(new SubModel2(data));
break;
default:
self.add(new BaseModel(data))
}
});
}
});
//Example Use
x = new ModelCollection;
x.parse([{type: 1},{type: 2}, {type: 99}]);
x.map(function(e){ return e.meth();});
回答2:
IMHO, I prefer to play with the model method in the collection:
var MyCollection = Backbone.Collection.extend({
model : function(attrs, options){
switch (attrs.type) {
case 1 :
model = new ModelType1(attrs, options);
break;
case 2 :
model = new ModelType2(attrs, options);
break;
default :
model = new BaseModel(attrs, options);
break;
}
}
});
or using a proxy model:
ProxyModel = Backbone.Model.extend({
initialize: function() {
switch (this.get('type')) {
case 1 :
model = new ModelType1(attrs, options);
break;
case 2 :
model = new ModelType2(attrs, options);
break;
default :
model = new BaseModel(attrs, options);
break;
}
return new model;
}
})
var MyCollection = Backbone.Collection.extend({
model : ProxyModel,
});
回答3:
I've just implemented this feature and started off with Jake Dempsey's advice, however you actually need to override a different method in the collection. This is the code I used:
var MyCollection = Backbone.Collection.extend({
_prepareModel: function(model, options) {
if (!(model instanceof Backbone.Model)) {
var attrs = model;
switch (attrs.type) {
case 1 :
model = new ModelType1(attrs, { collection: this });
break;
case 2 :
model = new ModelType2(attrs, { collection: this });
break;
default :
model = new BaseModel(attrs, { collection: this });
break;
}
if (model.validate && !model._performValidation(attrs, options)) model = false;
} else if (!model.collection) {
model.collection = this;
}
return model;
}
});
Note that it is a private(ish) method so something to keep in mind when upgrading Backbone.
来源:https://stackoverflow.com/questions/6061445/backbone-js-collection-with-specific-types