mirror of
https://github.com/jashkenas/backbone.git
synced 2026-01-23 13:58:06 -05:00
Merge pull request #936 from braddunbar/nested-change
fixes #915 - nested `'change:attr'` events
This commit is contained in:
22
backbone.js
22
backbone.js
@@ -233,15 +233,19 @@
|
||||
var now = this.attributes;
|
||||
var escaped = this._escapedAttributes;
|
||||
var prev = this._previousAttributes || {};
|
||||
var alreadyChanging = this._changing;
|
||||
var alreadySetting = this._setting;
|
||||
this._changed || (this._changed = {});
|
||||
this._changing = true;
|
||||
this._setting = true;
|
||||
|
||||
// Update attributes.
|
||||
for (attr in attrs) {
|
||||
val = attrs[attr];
|
||||
if (!_.isEqual(now[attr], val)) delete escaped[attr];
|
||||
options.unset ? delete now[attr] : now[attr] = val;
|
||||
if (this._changing && !_.isEqual(this._changed[attr], val)) {
|
||||
this.trigger('change:' + attr, this, val, options);
|
||||
this._moreChanges = true;
|
||||
}
|
||||
delete this._changed[attr];
|
||||
if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) {
|
||||
this._changed[attr] = val;
|
||||
@@ -249,9 +253,9 @@
|
||||
}
|
||||
|
||||
// Fire the `"change"` events, if the model has been changed.
|
||||
if (!alreadyChanging) {
|
||||
if (!alreadySetting) {
|
||||
if (!options.silent && this.hasChanged()) this.change(options);
|
||||
this._changing = false;
|
||||
this._setting = false;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
@@ -379,12 +383,20 @@
|
||||
// a `"change:attribute"` event for each changed attribute.
|
||||
// Calling this will cause all objects observing the model to update.
|
||||
change: function(options) {
|
||||
if (this._changing || !this.hasChanged()) return this;
|
||||
this._changing = true;
|
||||
this._moreChanges = true;
|
||||
for (var attr in this._changed) {
|
||||
this.trigger('change:' + attr, this, this._changed[attr], options);
|
||||
}
|
||||
this.trigger('change', this, options);
|
||||
while (this._moreChanges) {
|
||||
this._moreChanges = false;
|
||||
this.trigger('change', this, options);
|
||||
}
|
||||
this._previousAttributes = _.clone(this.attributes);
|
||||
delete this._changed;
|
||||
this._changing = false;
|
||||
return this;
|
||||
},
|
||||
|
||||
// Determine if the model has changed since the last `"change"` event.
|
||||
|
||||
@@ -506,17 +506,6 @@ $(document).ready(function() {
|
||||
a.set({state: 'hello'});
|
||||
});
|
||||
|
||||
test("Model: Multiple nested calls to set", function() {
|
||||
var counter = 0, model = new Backbone.Model({});
|
||||
model.on('change', function() {
|
||||
counter++;
|
||||
model.set({b: 1});
|
||||
model.set({a: 1});
|
||||
})
|
||||
.set({a: 1});
|
||||
equal(counter, 1, 'change is only triggered once');
|
||||
});
|
||||
|
||||
test("hasChanged/set should use same comparison", function() {
|
||||
expect(2);
|
||||
var changed = 0, model = new Backbone.Model({a: null});
|
||||
@@ -627,4 +616,53 @@ $(document).ready(function() {
|
||||
equal(changed, 1);
|
||||
});
|
||||
|
||||
test("nested `set` during `'change:attr'`", 1, function() {
|
||||
var model = new Backbone.Model();
|
||||
model.on('change:x', function() { ok(true); });
|
||||
model.on('change:y', function() {
|
||||
model.set({x: true});
|
||||
// only fires once
|
||||
model.set({x: true});
|
||||
});
|
||||
model.set({y: true});
|
||||
});
|
||||
|
||||
test("nested `change` only fires once", 1, function() {
|
||||
var model = new Backbone.Model();
|
||||
model.on('change', function() {
|
||||
ok(true);
|
||||
model.change();
|
||||
});
|
||||
model.set({x: true});
|
||||
});
|
||||
|
||||
test("no `'change'` event if no changes", function() {
|
||||
var model = new Backbone.Model();
|
||||
model.on('change', function() { ok(false); });
|
||||
model.change();
|
||||
});
|
||||
|
||||
test("nested `set` suring `'change'`", 3, function() {
|
||||
var count = 0;
|
||||
var model = new Backbone.Model();
|
||||
model.on('change', function() {
|
||||
switch(count++) {
|
||||
case 0:
|
||||
deepEqual(this.changedAttributes(), {x: true});
|
||||
model.set({y: true});
|
||||
break;
|
||||
case 1:
|
||||
deepEqual(this.changedAttributes(), {x: true, y: true});
|
||||
model.set({z: true});
|
||||
break;
|
||||
case 2:
|
||||
deepEqual(this.changedAttributes(), {x: true, y: true, z: true});
|
||||
break;
|
||||
default:
|
||||
ok(false);
|
||||
}
|
||||
});
|
||||
model.set({x: true});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user