diff --git a/backbone.js b/backbone.js index 7333a278d..5709b4b35 100644 --- a/backbone.js +++ b/backbone.js @@ -445,7 +445,7 @@ // If the server returns an attributes hash that differs, the model's // state will be `set` again. save: function(key, val, options) { - var attrs, method, xhr, attributes = this.attributes; + var attrs, method, xhr, dfd = Backbone.Deferred(), attributes = this.attributes; // Handle both `"key", value` and `{key: value}` -style arguments. if (key == null || typeof key === 'object') { @@ -461,9 +461,9 @@ // `set(attr).save(null, opts)` with validation. Otherwise, check if // the model will be valid when the attributes, if any, are set. if (attrs && !options.wait) { - if (!this.set(attrs, options)) return false; + if (!this.set(attrs, options)) return dfd.reject(); } else { - if (!this._validate(attrs, options)) return false; + if (!this._validate(attrs, options)) return dfd.reject(); } // Set temporary attributes if `{wait: true}`. @@ -519,7 +519,7 @@ if (this.isNew()) { options.success(); - return false; + return Backbone.Deferred().resolve(); } wrapError(this, options); @@ -1204,6 +1204,12 @@ return Backbone.$.ajax.apply(Backbone.$, arguments); }; + // Set the default promises library to proxy to `$`. + // Override this if you'd like to use a different library. + Backbone.Deferred = function() { + return Backbone.$.Deferred.apply(Backbone.$, arguments); + } + // Backbone.Router // --------------- diff --git a/test/model.js b/test/model.js index faede68a8..c82506841 100644 --- a/test/model.js +++ b/test/model.js @@ -489,7 +489,7 @@ $(document).ready(function() { ok(_.isEqual(this.syncArgs.model, doc)); var newModel = new Backbone.Model; - equal(newModel.destroy(), false); + equal(newModel.destroy().state(), 'resolved'); }); test("non-persisted destroy", 1, function() { @@ -959,10 +959,14 @@ $(document).ready(function() { var model = new Backbone.Model; model.validate = function(){ return 'invalid'; }; model.sync = function(){ ok(false); }; - strictEqual(model.save(), false); + model.save().then(function() { + ok(false); + }, function() { + ok(true); + }); }); - test("#1377 - Save without attrs triggers 'error'.", 1, function() { + test("#1377 - Save without attrs triggers 'error'.", 2, function() { var Model = Backbone.Model.extend({ url: '/test/', sync: function(method, model, options){ options.success(); }, @@ -970,7 +974,11 @@ $(document).ready(function() { }); var model = new Model({id: 1}); model.on('invalid', function(){ ok(true); }); - model.save(); + model.save().then(function() { + ok(false); + }, function() { + ok(true); + }); }); test("#1545 - `undefined` can be passed to a model constructor without coersion", function() { diff --git a/test/sync.js b/test/sync.js index 8fddb47fa..14f408548 100644 --- a/test/sync.js +++ b/test/sync.js @@ -155,6 +155,21 @@ $(document).ready(function() { Backbone.sync('create', model); }); + test("Backbone.Deferred", 2, function() { + Backbone.sync = function(settings){ + return Backbone.Deferred().resolve(); + }; + var model = new Backbone.Model(); + model.url = '/test'; + model.validate = function(attrs) { + return !attrs.accept; + } + var result = model.save({accept: false}); + strictEqual(result.state(), 'rejected'); + result = model.save({accept: true}); + strictEqual(result.state(), 'resolved'); + }); + test("Call provided error callback on error.", 1, function() { var model = new Backbone.Model; model.url = '/test';