如何在Backbone模型中强制执行属性类型?

时间:2012-04-06 06:43:19

标签: javascript backbone.js

我想要一个带有float属性的Backbone模型,但不要过多担心变量类型。

我想在模型中封装值解析,所以我想覆盖set函数:

var Place = Backbone.Model.extend({
  set: function(attributes, options) {
    if (!_.isEmpty(attributes.latitude)){
      attributes.latitude == parseFloat(attributes.latitude);
    }
    if (!_.isEmpty(attributes.longitude)){
      attributes.longitude == parseFloat(attributes.longitude);
    }
    Backbone.Model.prototype.set.call(this, attributes, options);
  }
});

然而,这似乎很麻烦,因为我在验证方法中会有类似的逻辑,并且可能在多个模型中重复。我不认为View应该处理这些转换。

那么最好的方法是什么?

2 个答案:

答案 0 :(得分:2)

为您的模型使用验证插件,以便您可以以通用方式验证输入。

有几个,包括我写的一个:

然后您不必担心在其他地方执行数据验证 - 您的模型会执行此操作并发送出您可以收听的error消息并提供适当的反馈。

此外,在极少数情况下,lat / lng对可以是整数,例如Greenwich England:0,0或北极:90,180。由于JavaScript只有“number”,因此parseFloat的任何有效输入对parseInt也有效。

但是parseFloat将始终返回一个浮点数。

答案 1 :(得分:0)

我的解决方案是用预处理器代理替换Backbone.Model.prototype.set

/**
 * Intercept calls to Backbone.Model.set and preprocess attribute values.
 * 
 * If the model has a <code>preprocess</code> property, that property will be
 * used for mapping attribute names to preprocessor functions. This is useful
 * for automatically converting strings to numbers, for instance.
 * 
 * @param Backbone
 *            the global Backbone object.
 */
(function(Backbone) {
    var originalSet = Backbone.Model.prototype.set;
    _.extend(Backbone.Model.prototype, {
        set: function(key, val, options) {
            if(!this.preprocess) {
                return originalSet.apply(this, arguments);
            }

            // If-else copied from Backbone source
            if (typeof key === 'object') {
                attrs = key;
                options = val;
            } else {
                (attrs = {})[key] = val;
            }

            for(attr in this.preprocess) {
                if(_.has(attrs, attr)) {
                    attrs[attr] = this.preprocess[attr](attrs[attr]);
                }
            }
            return originalSet.call(this, attrs, options);
        },
    });
})(Backbone);

在此之后,具有preprocess属性的模型将使用它将属性名称映射到预处理器函数。例如,preprocess: { age: parseInt }表示每当设置age属性时,该值将在实际设置之前通过parseInt传递。没有相应preprocess条目的属性不会受到影响。

使用示例:

var Thing = Backbone.Model.extend({
    preprocess: {
        mass: parseInt,
        created: function(s) { return new Date(s); },
    },
});
var t = new Thing({
    label: '42',
    mass: '42',
    created: '1971-02-03T12:13:14+02:00',
});
console.log(t.get('label')+3); // 423
console.log(t.get('mass')+3); // 45
console.log(t.get('created').toLocaleString('ja-JP', { weekday: 'short' })); // 水

赞成

  • 该功能适用​​于所有型号,无需重复代码
  • 无需在每次拨打{ validate: true }
  • 时发送set
  • 无需在validate中复制预处理,因为这是在调用validate之前发生的(这也可能是下面的内容)

缺点

  • Backbone代码的一些重复
  • 由于预处理在调用validate之前发生,因此可能会中断验证。 JavaScript解析方法通常返回无效值而不是抛出异常(即parseInt('foo')返回NaN),因此您应该能够检测到它。