Change event triggering on momentjs object

随声附和 提交于 2019-12-13 15:11:37

问题


For one of my projects, i'm listening on attribute changes on a model object and calling view methods if its attributes change.

Problem is one of the attribute of my model is a momentjs date object.

I've looked into backbone source and it seems it triggers changes in the setter using underscore method _.isEqual().

After reading underscore documentation, isEqual does a deep comparison of both objects.

Seems alright but momentjs object contains the initial formatting informations and even if the actual value of the date has the same meaning, if it comes from different place, it might be formatted differently and hence, be considered not equal by underscore deep comparison.

// 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')
});

How should i go about this ?

  • Store unix timestamp instead of momentjs object inside my model ? Which also means refactoring my whole code...
  • Find a way to "override" isEqual when it's a momentjs object ? But i would rather not modify underscore, modifying momentjs seems ok though.

回答1:


You best bet is probably to override the model.set method to perform a custom equality check on certain attributes.

Let's create EqualModels as our base class that holds the override:

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

The goal is to determine if an attribute has an equality check defined in this.equals, apply this function on the current and potential values and remove the attribute from the set attributes if the values are deemed equal.

You could then write you model as

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

Here the moment object is only updated when the DD/MM/YYYY formats are different. And a demo http://jsfiddle.net/Nwdf4/3/




回答2:


As nikoshr mentioned, it shouldn't really that much of a concern as the events are triggered only when you do a set or a fetch, and in both case, you can silent the events with the {silent: true} option. So, if you control the places where you might do an update on the date, you can implement your own comparison method here to decide if it should be silent or not, this way, no need to modify momentjs, underscore or your models.

That being said, I believe your first approach is actually 'correct', in the sense that it's probably not such a great idea to have instance of objects in your Models' values as Backbone will not automatically serialize them. I feel that it would be cleaner to store a timestamp or whatever simple time representation that contains all the relevant info in your model and in the model's parse(), transform this into a MomentJs object and store it in a property of the object. It means it won't be available through a model.get('momentJsObject') but through a model.momentJsObject and you can still access the source data, ready to be serialized back to the server, with a get('rawMoment'). Of course, it really depends on the situation and this would just be a 'general case' kind of thing for reference.



来源:https://stackoverflow.com/questions/20352846/change-event-triggering-on-momentjs-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!