是否有一种标准方法可以处理Backbone中的不可保存值。
e.g。
MyModel = Backbone.extend(Backbone.Model, {
initialize: function () {
this.set({'inches': this.get('mm') / 25});
}
})
如果我在这个模型上调用save(),它会抛出一个错误,因为inches
没有相应的数据库字段。我可以想出几种方法来解决这个问题,但我想知道是否有一种经过试验和测试的方法通常最适合用于此目的?
目前我的首选解决方案是扩展Backbone的toJSON
方法并允许传递布尔参数dontCleanup
以允许它仍然返回所有模型的值(包括不可保存的值)当它需要时,例如传递给模板。
答案 0 :(得分:11)
我喜欢Peter Lyon的想法。我已经考虑了几次,但从未真正把它放到位。不过,对于我处理此问题的所有方法,这是我的两个最爱:
这很简单:不要在模型的标准属性中存储所需的值。而是将其直接附加到对象:
myModel.someValue = "some value";
这里的一个大问题是,您没有获得与在模型上调用set
相关的所有事件。所以我倾向于将它包装在一个为我做所有事情的方法中。例如,我在模型上使用的常用方法是select
,表示已选择此模型:
MyModel = Backbone.Model.extend({
select: function(){
if (!this.selected){
this.selected = true;
this.trigger("change:selected", this, this.selected);
}
}
});
在你的情况下,我不确定这是一个好方法。您的数据需要根据属性中的值进行计算。
为此,我倾向于使用视图模型。
基本思想是,您可以像往常一样创建一个可持久的骨干模型。但是你来了并创建了另一个继承了原始模型的模型,并添加了你需要的所有数据。
有很多方法可以做到这一点。这可能是一个非常简单的版本:
MyModel = Backbone.Model.Extend({ ... });
MyViewModel = function(model){
var viewModel = Object.create(model);
viewModel.toJSON = function(){
var json = model.toJSON();
json.inches = json.mm / 25;
return json;
};
return viewModel;
});
使用Object.create
包装它的最大好处是,您现在具有原型继承情况,因此模型中的所有标准功能仍然存在。我们刚刚在视图模型上重写了toJSON
方法,以便它返回带有inches
属性的JSON对象。
然后在需要此视图的视图中,您可以将模型包装在initialize函数中:
MyView = Backbone.View.extend({
initialize: function(){
this.model = MyViewModel(this.model);
},
render: function(){
var data = this.model.toJSON(); // returns with inches
}
});
如果你愿意,可以调用inches
,但最终不会做任何不同的事情,因为我们明确地从new MyViewModel(this.model)
函数返回一个对象实例。
当您的视图的渲染方法调用MyViewModel
时,您将获得toJSON
属性。
当然,这个实现存在一些潜在的内存问题和性能问题,但是这些可以通过一些更好的代码用于视图模型来轻松解决。不过,这个快速而肮脏的例子应该让你走上正轨。
答案 1 :(得分:3)
我认为应该这样做。将您的Model
defaults
定义为有效架构,然后仅返回this.attributes
期间有效的toJSON
子集。
var Model = Backbone.Model.extend({
defaults: {
foo: 42,
bar: "bar"
},
toJSON: function () {
var schemaKeys = _.keys(this.defaults);
var allowedAttributes = {};
_.each(this.attributes, function (value, key) {
if (_.include(schemaKeys, key)) {
allowedAttributes[key] = value;
}
return allowedAttributes;
}
});
请注意,一旦您拥有下划线1.3.3,_.pick
会使代码缩短一些。在我通过骨干社区的旅行中,我没有看到“经过试验和测试”的约定,并且由于主干留下了很多选项,有时会出现惯例,但我们会看到这个stackoverflow问题产生了什么。
答案 2 :(得分:2)
在Backbone.js中处理非持久属性已经有一段时间了,特别是因为我已经开始使用ember / ember-data,它通过计算处理各种情况属性,余烬数据属性或控制器。
许多解决方案建议自定义toJSON
方法。但是,一些流行的Backbone插件(特别是那些处理嵌套模型的插件)实现了自己的toJSON
方法,并调用Backbone.Model.prototype.toJSON
来获取模型属性的对象表示。因此,通过覆盖模型定义中的toJSON
方法,您将失去这些插件的一些(可能至关重要的)功能。
我提出的最好的方法是在模型定义中包含excludeFromJSON
个数组,并在toJSON
本身上覆盖Backbone.Model.prototype
方法:
Backbone.Model.prototype.toJSON = function() {
var json = _.clone(this.attributes),
excludeFromJSON = this.excludeFromJSON;
if(excludeFromJSON) {
_.each(excludeFromJSON, function(key) {
delete json[key];
});
}
return json;
};
MyModel = Backbone.Model.extend({
excludeFromJSON: [
'inches'
]
});
通过这种方式,您只需要定义非持久密钥(如果您忘记这样做,当服务器抛出错误时,您很快就会被提醒!)。如果不存在toJSON
属性,excludeFromJSON
将表现正常。
在您的情况下,inches
是一个计算属性,派生自mm
,因此将其作为模型上的方法实现是有意义的(确保mm为mm时的值是正确的)改变后的):
MyModel = Backbone.Model.extend({
inches: function() {
return this.get('mm') / 25;
}
});
然而,这具有与每个其他属性不同地访问的缺点。理想情况下,您希望保持与访问其他属性的一致性。这可以通过extending the default get
method:
var getMixin = {
get: function(attr) {
if(typeof this[attr] == 'function') {
return this[attr]();
}
return Backbone.Model.prototype.get.call(this, attr);
}
};
MyModel = Backbone.Model.extend({
inches: function() {
return this.get('mm') / 25;
}
});
_.extend(MyModel.prototype, getMixin);
您可以这样做:
new MyModel().get('inches');
此方法不会触及基础attributes
哈希,这意味着inches
不会出现在toJSON
表示中,除非您{{1}以后set
的值,在这种情况下,您需要类似inches
数组的内容。
如果您需要excludeFromJSON
set
值,您可能还想听取更改并调整inches
的值:
mm
值得注意的是,the (official?) purpose of the toJSON
method最近被重新定义为准备与服务器同步的模型。因此,调用MyModel = Backbone.Model.extend({
initialize: function() {
this.on('change:inches', this.changeInches, this);
},
inches: function() {
return this.get('mm') / 25;
},
changeInches: function() {
this.set('mm', this.attributes.inches * 25);
}
});
_.extend(MyModel.prototype, getMixin);
应始终只返回“可持久”(或“可保存”)属性。