7

是否有标准方法来处理 Backbone 中的不可保存值。

例如

MyModel = Backbone.extend(Backbone.Model, {
    initialize: function () {
        this.set({'inches': this.get('mm') / 25});
    }
})

如果我在这个模型上调用 save() ,它会抛出一个错误,因为没有对应的数据库字段inches。我可以想出几种方法来解决这个问题,但我想知道是否有一种经过试验和测试的方法通常最适合这个问题?

目前,我首选的解决方案是扩展 Backbone 的toJSON方法并允许传递布尔参数dontCleanup以允许它在需要时返回所有模型的值(包括不可保存的值),例如传递给模板。

4

3 回答 3

11

我喜欢彼得里昂的想法。我曾想过几次,但从未真正落实到位。不过,对于我处理此问题的所有方式,以下是我最喜欢的两个:

  • 非“属性”值
  • 查看模型

非属性值

这很简单:不要将您需要的值存储在模型的标准属性中。相反,将其直接附加到对象:


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 对象。

然后在需要这个的视图中,您可以将模型包装在初始化函数中:


MyView = Backbone.View.extend({
  initialize: function(){
    this.model = MyViewModel(this.model);
  },

render: function(){ var data = this.model.toJSON(); // returns with inches } });

您可以new MyViewModel(this.model)根据需要调用,但最终不会有任何不同,因为我们从MyViewModel函数中显式返回了一个对象实例。

当你的视图的渲染方法调用toJSON时,你会得到inches它的属性。

当然,此实现存在一些潜在的内存问题和性能问题,但可以通过一些更好的视图模型代码轻松解决这些问题。不过,这个快速而肮脏的例子应该会让你走上正轨。

于 2012-05-06T14:30:37.073 回答
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;
  }
});

请注意,_.pick一旦您有下划线 1.3.3 可用,这会使代码更短。在我的骨干社区旅行中,我还没有看到“经过验证的”约定,并且由于骨干留下了很多选择,有时约定不会出现,但我们会看到这个 stackoverflow 问题会产生什么。

于 2012-05-06T13:48:04.240 回答
2

处理 Backbone.js 中的非持久属性已经有一段时间了,特别是自从我开始使用 ember/ember-data 之后,它通过计算属性、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 时英寸的值是正确的):

MyModel = Backbone.Model.extend({
  inches: function() {
    return this.get('mm') / 25;
  }
});

但是,这具有与其他所有属性不同的访问方式的缺点。理想情况下,您会希望使其与访问其他属性保持一致。这可以通过扩展默认get方法来实现:

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表示中,除非你稍后set的值inches,在这种情况下你需要像excludeFromJSON数组这样的东西。

如果您需要setinches值,您可能还想监听变化并调整 的值mm

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);

请参阅JSBin 上的完整示例

还值得注意的是,该方法的(官方?)目的toJSON最近被重新定义为准备与服务器同步的模型。出于这个原因,调用toJSON应该始终只返回“持久”(或“可保存”)属性。

于 2013-06-07T13:22:25.283 回答