mirror of
https://github.com/jashkenas/backbone.git
synced 2026-01-23 13:58:06 -05:00
Merge branch 'set-unset-clear' of https://github.com/braddunbar/backbone
This commit is contained in:
74
backbone.js
74
backbone.js
@@ -187,6 +187,7 @@
|
||||
options || (options = {});
|
||||
if (!attrs) return this;
|
||||
if (attrs.attributes) attrs = attrs.attributes;
|
||||
if (options.unset) for (var attr in attrs) attrs[attr] = void 0;
|
||||
var now = this.attributes, escaped = this._escapedAttributes;
|
||||
|
||||
// Run validation.
|
||||
@@ -202,8 +203,8 @@
|
||||
// Update attributes.
|
||||
for (var attr in attrs) {
|
||||
var val = attrs[attr];
|
||||
if (!_.isEqual(now[attr], val)) {
|
||||
now[attr] = val;
|
||||
if (!_.isEqual(now[attr], val) || options.unset && (attr in now)) {
|
||||
options.unset ? delete now[attr] : now[attr] = val;
|
||||
delete escaped[attr];
|
||||
this._changed = true;
|
||||
if (!options.silent) this.trigger('change:' + attr, this, val, options);
|
||||
@@ -220,53 +221,20 @@
|
||||
|
||||
// Remove an attribute from the model, firing `"change"` unless you choose
|
||||
// to silence it. `unset` is a noop if the attribute doesn't exist.
|
||||
unset : function(attr, options) {
|
||||
if (!(attr in this.attributes)) return this;
|
||||
options || (options = {});
|
||||
var value = this.attributes[attr];
|
||||
|
||||
// Run validation.
|
||||
var validObj = {};
|
||||
validObj[attr] = void 0;
|
||||
if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
|
||||
|
||||
// changedAttributes needs to know if an attribute has been unset.
|
||||
(this._unsetAttributes || (this._unsetAttributes = [])).push(attr);
|
||||
|
||||
// Remove the attribute.
|
||||
delete this.attributes[attr];
|
||||
delete this._escapedAttributes[attr];
|
||||
if (attr == this.idAttribute) delete this.id;
|
||||
this._changed = true;
|
||||
if (!options.silent) {
|
||||
this.trigger('change:' + attr, this, void 0, options);
|
||||
this.change(options);
|
||||
unset : function(attrs, options) {
|
||||
if (_.isString(attrs)) {
|
||||
var args = _.toArray(arguments), attrs = {};
|
||||
while (_.isString(options = args.shift())) attrs[options] = void 0;
|
||||
}
|
||||
return this;
|
||||
(options || (options = {})).unset = true;
|
||||
return this.set(attrs, options);
|
||||
},
|
||||
|
||||
// Clear all attributes on the model, firing `"change"` unless you choose
|
||||
// to silence it.
|
||||
clear : function(options) {
|
||||
options || (options = {});
|
||||
var attr;
|
||||
var old = this.attributes;
|
||||
|
||||
// Run validation.
|
||||
var validObj = {};
|
||||
for (attr in old) validObj[attr] = void 0;
|
||||
if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
|
||||
|
||||
this.attributes = {};
|
||||
this._escapedAttributes = {};
|
||||
this._changed = true;
|
||||
if (!options.silent) {
|
||||
for (attr in old) {
|
||||
this.trigger('change:' + attr, this, void 0, options);
|
||||
}
|
||||
this.change(options);
|
||||
}
|
||||
return this;
|
||||
var keys = _.without(_.keys(this.attributes), 'id');
|
||||
return this.unset.apply(this, keys.concat([options]));
|
||||
},
|
||||
|
||||
// Fetch the model from the server. If the server's representation of the
|
||||
@@ -346,7 +314,6 @@
|
||||
change : function(options) {
|
||||
this.trigger('change', this, options);
|
||||
this._previousAttributes = _.clone(this.attributes);
|
||||
this._unsetAttributes = null;
|
||||
this._changed = false;
|
||||
},
|
||||
|
||||
@@ -362,23 +329,16 @@
|
||||
// view need to be updated and/or what attributes need to be persisted to
|
||||
// the server. Unset attributes will be set to undefined.
|
||||
changedAttributes : function(now) {
|
||||
if (!this._changed) return false;
|
||||
now || (now = this.attributes);
|
||||
var old = this._previousAttributes, unset = this._unsetAttributes;
|
||||
|
||||
var changed = false;
|
||||
var changed = false, old = this._previousAttributes;
|
||||
for (var attr in now) {
|
||||
if (!_.isEqual(old[attr], now[attr])) {
|
||||
changed || (changed = {});
|
||||
changed[attr] = now[attr];
|
||||
}
|
||||
if (_.isEqual(old[attr], now[attr])) continue;
|
||||
(changed || (changed = {}))[attr] = now[attr];
|
||||
}
|
||||
|
||||
if (unset) {
|
||||
changed || (changed = {});
|
||||
var len = unset.length;
|
||||
while (len--) changed[unset[len]] = void 0;
|
||||
for (var attr in old) {
|
||||
if (!(attr in now)) (changed || (changed = {}))[attr] = void 0;
|
||||
}
|
||||
|
||||
return changed;
|
||||
},
|
||||
|
||||
|
||||
@@ -140,19 +140,24 @@ $(document).ready(function() {
|
||||
});
|
||||
|
||||
test("Model: set and unset", function() {
|
||||
expect(8);
|
||||
attrs = {id: 'id', foo: 1, bar: 2, baz: 3};
|
||||
a = new Backbone.Model(attrs);
|
||||
var changeCount = 0;
|
||||
a.bind("change:foo", function() { changeCount += 1; });
|
||||
a.set({'foo': 2});
|
||||
ok(a.get('foo')== 2, "Foo should have changed.");
|
||||
ok(a.get('foo') == 2, "Foo should have changed.");
|
||||
ok(changeCount == 1, "Change count should have incremented.");
|
||||
a.set({'foo': 2}); // set with value that is not new shouldn't fire change event
|
||||
ok(a.get('foo')== 2, "Foo should NOT have changed, still 2");
|
||||
ok(a.get('foo') == 2, "Foo should NOT have changed, still 2");
|
||||
ok(changeCount == 1, "Change count should NOT have incremented.");
|
||||
|
||||
a.unset('foo');
|
||||
ok(a.get('foo')== null, "Foo should have changed");
|
||||
a.validate = function(attrs) {
|
||||
ok(attrs.foo === void 0, 'ignore values when unsetting');
|
||||
};
|
||||
a.unset({foo: 1});
|
||||
ok(a.get('foo') == null, "Foo should have changed");
|
||||
delete a.validate;
|
||||
ok(changeCount == 2, "Change count should have incremented for unset.");
|
||||
|
||||
a.unset('id');
|
||||
@@ -199,11 +204,18 @@ $(document).ready(function() {
|
||||
|
||||
test("Model: clear", function() {
|
||||
var changed;
|
||||
var model = new Backbone.Model({name : "Model"});
|
||||
var model = new Backbone.Model({id: 1, name : "Model"});
|
||||
model.bind("change:name", function(){ changed = true; });
|
||||
model.bind("change", function() {
|
||||
var changedAttrs = model.changedAttributes();
|
||||
ok('name' in changedAttrs);
|
||||
ok(!('id' in changedAttrs));
|
||||
});
|
||||
model.clear();
|
||||
equals(changed, true);
|
||||
equals(model.get('name'), undefined);
|
||||
equals(model.id, 1);
|
||||
equals(model.get('id'), 1);
|
||||
});
|
||||
|
||||
test("Model: defaults", function() {
|
||||
@@ -450,12 +462,14 @@ $(document).ready(function() {
|
||||
});
|
||||
|
||||
test("Model: Multiple nested calls to set", function() {
|
||||
var model = new Backbone.Model({});
|
||||
var counter = 0, model = new Backbone.Model({});
|
||||
model.bind('change', function() {
|
||||
counter++;
|
||||
model.set({b: 1});
|
||||
model.set({a: 1});
|
||||
})
|
||||
.set({a: 1});
|
||||
equal(counter, 1, 'change is only triggered once');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user