Merging in Chris Lloyd's Model#clear. Making validations run, if defined, on Model#clear and Model#unset

This commit is contained in:
Jeremy Ashkenas
2010-11-01 11:43:05 -04:00
parent 47ba5c6552
commit b4d573c943
2 changed files with 71 additions and 20 deletions

View File

@@ -142,19 +142,8 @@
if (attrs.attributes) attrs = attrs.attributes;
var now = this.attributes;
// Run validation if `validate` is defined. If a specific `error` callback
// has been passed, call that instead of firing the general `"error"` event.
if (this.validate) {
var error = this.validate(attrs);
if (error) {
if (options.error) {
options.error(this, error);
} else {
this.trigger('error', this, error);
}
return false;
}
}
// Run validation.
if (this.validate && !this._performValidation(attrs, options)) return false;
// Check for changes of `id`.
if ('id' in attrs) this.id = attrs.id;
@@ -176,25 +165,38 @@
return this;
},
// Remove an attribute from the model, firing `"change"` unless you choose to
// silence it.
// Remove an attribute from the model, firing `"change"` unless you choose
// to silence it.
unset : function(attr, options) {
options || (options = {});
var value = this.attributes[attr];
// Run validation.
var validObj = {};
validObj[attr] = void 0;
if (this.validate && !this._performValidation(validObj, options)) return false;
// Remove the attribute.
delete this.attributes[attr];
if (!options.silent) {
this._changed = true;
this.trigger('change:' + attr, this);
this.change();
}
return value;
return this;
},
// Clears all attributes from the model, firing `"change"` unless you
// choose to silence it.
// Clear all attributes on the model, firing `"change"` unless you choose
// to silence it.
clear : function(options) {
options || (options = {});
var old = this.attributes;
// Run validation.
var validObj = {};
for (attr in old) validObj[attr] = void 0;
if (this.validate && !this._performValidation(validObj, options)) return false;
this.attributes = {};
if (!options.silent) {
this._changed = true;
@@ -203,7 +205,7 @@
}
this.change();
}
return old;
return this;
},
// Fetch the model from the server. If the server's representation of the
@@ -322,6 +324,22 @@
// `"change"` event.
previousAttributes : function() {
return _.clone(this._previousAttributes);
},
// Run validation against a set of incoming attributes, returning `true`
// if all is well. If a specific `error` callback has been passed,
// call that instead of firing the general `"error"` event.
_performValidation : function(attrs, options) {
var error = this.validate(attrs);
if (error) {
if (options.error) {
options.error(this, error);
} else {
this.trigger('error', this, error);
}
return false;
}
return true;
}
});

View File

@@ -102,6 +102,15 @@ $(document).ready(function() {
equals(model.get('name'), '');
});
test("Model: clear", function() {
var changed;
var model = new Backbone.Model({name : "Model"});
model.bind("change:name", function(){ changed = true; });
model.clear();
equals(changed, true);
equals(model.get('name'), undefined);
});
test("Model: changed, hasChanged, changedAttributes, previous, previousAttributes", function() {
var model = new Backbone.Model({name : "Tim", age : 10});
model.bind('change', function() {
@@ -153,6 +162,30 @@ $(document).ready(function() {
equals(lastError, "Can't change admin status.");
});
test("Model: validate on unset and clear", function() {
var error;
var model = new Backbone.Model({name: "One"});
model.validate = function(attrs) {
if ("name" in attrs) {
if (!attrs.name) {
error = true;
return "No thanks.";
}
}
};
model.set({name: "Two"});
equals(model.get('name'), 'Two');
equals(error, undefined);
model.unset('name');
equals(error, true);
equals(model.get('name'), 'Two');
model.clear();
equals(model.get('name'), 'Two');
delete model.validate;
model.clear();
equals(model.get('name'), undefined);
});
test("Model: validate with error callback", function() {
var lastError, boundError;
var model = new Backbone.Model();