Fixes #1016 - Expose changed.

* Expose `changed` as a public hash of changes.
* Initialize `changed`, `_pending`, and `_silent`
  in the constructor instead of checking in `set`.
* Ensure `changed` matches `attributes`.
This commit is contained in:
Brad Dunbar
2012-02-17 06:36:13 -05:00
parent 82ee97d36b
commit afbca7229c

View File

@@ -174,10 +174,14 @@
this.attributes = {};
this._escapedAttributes = {};
this.cid = _.uniqueId('c');
this.changed = {};
this._silent = {};
this._pending = {};
this.set(attributes, {silent: true});
delete this._changed;
delete this._silent;
delete this._pending;
// Reset change tracking.
this.changed = {};
this._silent = {};
this._pending = {};
this._previousAttributes = _.clone(this.attributes);
this.initialize.apply(this, arguments);
};
@@ -186,7 +190,7 @@
_.extend(Backbone.Model.prototype, Backbone.Events, {
// A hash of attributes whose current and previous value differ.
_changed: null,
changed: null,
// A hash of attributes that have silently changed since the last time
// `change` was called. Will become pending attributes on the next call.
@@ -252,13 +256,10 @@
// Check for changes of `id`.
if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
var changes = {};
var changes = options.changes = {};
var now = this.attributes;
var escaped = this._escapedAttributes;
var prev = this._previousAttributes || {};
this._changed || (this._changed = {});
this._silent || (this._silent = {});
this._pending || (this._pending = {});
for (attr in attrs) {
val = attrs[attr];
@@ -272,16 +273,16 @@
// If the new and previous value differ, record the change. If not,
// then remove changes for this attribute.
if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) {
this._changed[attr] = true;
this.changed[attr] = val;
if (!options.silent) this._pending[attr] = true;
} else {
delete this._changed[attr];
delete this.changed[attr];
delete this._pending[attr];
}
}
// Fire the `"change"` events.
if (!options.silent) this.change(_.extend({changes: changes}, options));
if (!options.silent) this.change(options);
return this;
},
@@ -412,20 +413,23 @@
var changing = this._changing;
this._changing = true;
// Silent changes become pending changes.
this._pending = _.extend(this._pending || {}, this._silent);
for (var attr in this._silent) this._pending[attr] = true;
// Silent changes are triggered.
var changes = _.extend({}, options.changes, this._silent);
delete this._silent;
this._silent = {};
for (var attr in changes) {
this.trigger('change:' + attr, this, this.attributes[attr], options);
}
if (changing) return this;
// Continue firing `"change"` events while there are pending changes.
while (!_.isEmpty(this._pending)) {
delete this._pending;
this._pending = {};
this.trigger('change', this, options);
// Pending and silent changes still remain.
this._changed = _.extend({}, this._pending, this._silent);
for (var attr in this.changed) {
if (this._pending[attr] || this._silent[attr]) continue;
delete this.changed[attr];
}
this._previousAttributes = _.clone(this.attributes);
}
this._changing = false;
@@ -435,8 +439,8 @@
// Determine if the model has changed since the last `"change"` event.
// If you specify an attribute name, determine if that attribute has changed.
hasChanged: function(attr) {
if (!arguments.length) return !_.isEmpty(this._changed);
return this._changed && _.has(this._changed, attr);
if (!arguments.length) return !_.isEmpty(this.changed);
return _.has(this.changed, attr);
},
// Return an object containing all the attributes that have changed, or
@@ -446,12 +450,7 @@
// You can also pass an attributes object to diff against the model,
// determining if there *would be* a change.
changedAttributes: function(diff) {
if (!diff) {
if (!this.hasChanged()) return false;
var changes = {};
for (var attr in this._changed) changes[attr] = this.attributes[attr];
return changes;
}
if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
var val, changed = false, old = this._previousAttributes;
for (var attr in diff) {
if (_.isEqual(old[attr], (val = diff[attr]))) continue;