更改事件触发momentjs对象

时间:2013-12-03 13:45:36

标签: javascript backbone.js underscore.js momentjs

对于我的一个项目,我正在监听模型对象的属性更改,并在其属性发生变化时调用视图方法。

问题是我的模型的一个属性是一个momentjs日期对象。

我查看了主干来源,似乎它使用下划线方法_.isEqual()触发了setter中的更改。

阅读underscore documentation后,isEqual会对两个对象进行深入比较。

似乎没有问题,但是momentjs对象包含初始格式化信息,即使日期的实际值具有相同的含义,如果它来自不同的地方,它的格式可能不同,因此,通过下划线深度比较可以认为不相等

// initialize model
var today = moment().startOf('day');

var model = new Backbone.Model({
    start: today
});

// change event
model.on('change', function(e){
    // if property start has changed
    if(e.changed.hasOwnProperty('start')){
        // log it
        console.log('date changed');   
    }
});


// simulates input from user
var userInput = moment().startOf('day').format('DD/MM/YYYY');
model.set({
    // it's the same day as today and it shouldn't trigger a change !
    start: moment(userInput,'DD/MM/YYYY')
});

我应该怎么做?

  • 在我的模型中存储unix时间戳而不是momentjs对象?这也意味着重构我的整个代码......
  • 找到一种方法来"覆盖" isEqual什么时候它是一个对象?但我宁愿不修改下划线,修改momentjs似乎还不错。

2 个答案:

答案 0 :(得分:3)

您最好的选择是覆盖model.set方法以对某些属性执行自定义相等检查。

让我们创建EqualModels作为保存覆盖的基类:

var EqualModels = Backbone.Model.extend({
    set: function(key, val, options) {
        if (!this.equals)
            return Backbone.Model.prototype.set.apply(this, arguments);

        //lifted from Backbone source code
        var attrs, attr, dropped, fn;
        if (key == null) return this;

        // Handle both `"key", value` and `{key: value}` -style arguments.
        if (typeof key === 'object') {
            attrs = key;
            options = val;
        } else {
            (attrs = {})[key] = val;
        }
        options || (options = {});

        //determine which attributes have a custom equality check and apply it
        dropped = [];
        for (attr in attrs) {
            fn = this.equals[attr];
            if (_.isFunction(fn)) {
                if (fn(this.attributes[attr], attrs[attr]))
                    dropped.push(attr);
            }
        }

        //remove the attributes that are deemed equal
        attrs = _.omit(attrs, dropped);

        return Backbone.Model.prototype.set.call(this, attrs, options);
    }
});

目标是确定某个属性是否在this.equals中定义了相等性检查,对当前值和潜在值应用此函数,如果认为值相等,则从集合属性中删除该属性。

然后您可以将模型写为

var M = EqualModels.extend({
    equals: {
        start: function(v1, v2) {
            if (typeof(v1)!==typeof(v2)) return false;
            return v1.format('DD/MM/YYYY')===v2.format('DD/MM/YYYY');
        }
    }
});

此处仅在 DD / MM / YYYY 格式不同时更新时刻对象。还有一个演示http://jsfiddle.net/Nwdf4/3/

答案 1 :(得分:0)

正如nikoshr所提到的那样,它不应该引起太大的关注,因为事件仅在您执行set或fetch时触发,并且在这两种情况下,您都可以使用{silent:true}选项使事件静音。因此,如果您控制可能对日期进行更新的位置,您可以在此处实现自己的比较方法,以确定它是否应该是静默的,这样,无需修改momentjs,下划线或模型。 / p>

话虽如此,我相信你的第一种方法实际上是'正确',因为在你的模型的值中拥有对象实例可能不是一个好主意,因为Backbone不会自动序列化它们。我觉得存储时间戳或包含模型和模型的parse()中所有相关信息的简单时间表示会更清晰,将其转换为MomentJs对象并将其存储在对象的属性中。这意味着它不能通过model.get('momentJsObject')获得,而是通过model.momentJsObject,你仍然可以使用get('rawMoment')访问源数据,准备序列化回服务器。当然,这实际上取决于具体情况,这只是一种“一般情况”的参考。