我有一个相当通用的模型和该模型的集合(见下文)我正在处理作为一系列视图的基础。在几个视图中,其中一个模型的选择会生成动作(通过'selected'属性),我需要能够在客户端跟踪选择。
然而,似乎在Backbone中没有干净的方法来做到这一点。在客户端上模型上添加/更改的任何属性都将同步到服务器。更改该属性时我无法使用{silent : yes}
,因为当change
事件触发该属性时,我需要触发视图中的更改。我提出的唯一方法是覆盖save
Backbone.Model
函数
我的问题:有没有办法让我缺少客户端唯一的属性,或者我的方法在其他方面存在结构上的缺陷,我只是没有看到?
var CSEvent = Backbone.Model.extend({
idAttribute: "_id",
urlRoot : '/api/events',
defaults: {
title : "",
type : "Native",
repeatOrOneTime : "OneTime",
selected : false
}
});
var CSEventCollection = Backbone.Collection.extend({
model: CSEvent,
url: '/api/events',
getSelectedEvent : function() {
return this.find(function(csevent) { return csevent.get('selected') === true; });
},
selectEvent : function(eventId) {
this.deselectEvent();
this.get(eventId).set({selected : true});
},
deselectEvent : function() {
this.getSelectedEvent().set({selected : false});
}
});
答案 0 :(得分:5)
尝试覆盖Model.toJSON()
方法,正如您在Backbone Model code中看到的那样,这种方法并不复杂。官方文件还建议在有特殊需要的情况下覆盖它。
尝试这样的事情:
var CSEvent = Backbone.Model.extend({
toJSON: function(){
return _.clone( _.pick( this.attributes, "title", "type", "repeatOrOneTime" ) );
}
});
答案 1 :(得分:1)
我不建议覆盖Model.toJSON()
,因为您可能希望在代码的其他部分使用JSON表示,例如将Backbone模型传递给微模板时。
自定义保存哪些属性的更好方法是覆盖Model对象中的the sync method:
sync: function(method, model, options) {
if (method == 'update' || method == 'create') {
var newModel = this.clone();
newModel.unset('ignoredAttribute', {silent: true);
return Backbone.sync.call(newModel, method, newModel, options);
} else {
return Backbone.sync.call(this, method, this, options);
}
},
此示例忽略名为ignoredAttribute
的属性。
在你的代码中它将是这样的:
var CSEvent = Backbone.Model.extend({
idAttribute: "_id",
urlRoot : '/api/events',
defaults: {
title : "",
type : "Native",
repeatOrOneTime : "OneTime",
selected : false
},
sync: function(method, model, options) {
if (method == 'update' || method == 'create') {
var newModel = this.clone();
newModel.unset('selected', {silent: true);
return Backbone.sync.call(newModel, method, newModel, options);
} else {
return Backbone.sync.call(this, method, this, options);
}
}
});
另一个简单的选择是将selected
作为对象属性(而不是作为Backbone托管属性),您仍然可以使用trigger
触发更改事件。对我来说,这是解决问题的最简单方法:
var CSEvent = Backbone.Model.extend({
idAttribute: "_id",
urlRoot : '/api/events',
selected : false,
defaults: {
title : "",
type : "Native",
repeatOrOneTime : "OneTime"
},
select: function() {
this.selected = true;
this.trigger('selected'); // you can use another event name here, ie. change
},
deselect: function() {
this.selected = false;
this.trigger('deselected');
}
});
答案 2 :(得分:1)
我想提出一种方法来实现这一点,使用上述两个原则,而不是覆盖toJSON()
。
此模式提供了许多好处 - 例如,在仅向服务器发送必要的属性之前,对所有属性进行客户端验证。
采用以下示例,我们为用户提供了更改密码的方法。
var UserModel = Backbone.Model.extend({
defaults: {
username: "",
password: {
old: "",
new: "",
confirm: ""
}
},
validate: function(attrs) {
if (attrs.password.new !== attrs.password.confirm) {
return "Passwords must match";
}
},
toRemoteJSON: function() {
var payload = this.toJSON();
delete payload.password.confirm;
return payload;
},
sync: function(method, model, options) {
if (method == 'update' || method == 'create') {
var newModel = this.clone();
newModel.clear({ silent: true });
newModel.set(this.toRemoteJSON(), { silent: true });
return Backbone.sync.call(newModel, method, newModel, options);
} else {
return Backbone.sync.call(this, method, this, options);
}
}
});
当模型为save
d时,在validate()
方法之前调用sync()
方法,因此它会检查两个新密码是否匹配。 sync
方法然后委托给toRemoteJSON()
,在发送到服务器之前删除确认密码字段。
toRemoteJSON()
函数只是抽象迭戈解决方案中的newModel.unset()
部分。但现在这意味着您可以在基础对象中定义被覆盖的sync
,而从此扩展的对象只需要定义toRemoteJSON()
。