Change event triggering on momentjs object
backbone.js, javascript, momentjs, underscore.js
Solution
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/
Problem
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.