Skip to content
Snippets Groups Projects
Commit b488e159 authored by totten's avatar totten
Browse files

CRM-12923, CRM-12943 - Track whether model changes have been saved to the server

----------------------------------------
* CRM-12923: Create HTML prototype of "Job Position" UI
  http://issues.civicrm.org/jira/browse/CRM-12923
* CRM-12943: Make HTML prototype of job UI functional
  http://issues.civicrm.org/jira/browse/CRM-12943
parent f1e8157b
No related branches found
No related tags found
No related merge requests found
......@@ -112,6 +112,78 @@
});
};
/**
* Configure a model class to track whether a model has unsaved changes.
*
* The ModelClass will be extended with:
* - Method: isSaved() - true if there have been no changes to the data since the last fetch or save
* - Event: saved(object model, bool is_saved) - triggered whenever isSaved() value would change
*
* Note: You should not directly call isSaved() within the context of the success/error/sync callback;
* I haven't found a way to make isSaved() behave correctly within these callbacks without patching
* Backbone. Instead, attach an event listener to the 'saved' event.
*
* @param ModelClass
*/
CRM.Backbone.trackSaved = function(ModelClass) {
// Retain references to some of the original class's functions
var Parent = _.pick(ModelClass.prototype, 'initialize', 'save', 'fetch');
// Private callback
var onSyncSuccess = function() {
this._modified = false;
if (this._oldModified.length > 0) {
this._oldModified.pop();
}
this.trigger('saved', this, this.isSaved());
};
var onSaveError = function() {
if (this._oldModified.length > 0) {
this._modified = this._oldModified.pop();
this.trigger('saved', this, this.isSaved());
}
};
// Defaults - if specified in ModelClass, preserve
_.defaults(ModelClass.prototype, {
isSaved: function() {
var result = !this.isNew() && !this._modified;
return result;
},
_saved_onchange: function(model, options) {
if (options.parse) return;
var oldModified = this._modified;
this._modified = true;
if (!oldModified) {
this.trigger('saved', this, this.isSaved());
}
}
});
// Overrides - if specified in ModelClass, replace
_.extend(ModelClass.prototype, {
initialize: function(options) {
this._modified = false;
this._oldModified = [];
this.listenTo(this, 'change', this._saved_onchange);
this.listenTo(this, 'error', onSaveError);
this.listenTo(this, 'sync', onSyncSuccess);
if (Parent.initialize) {
return Parent.initialize.apply(this, arguments);
}
},
save: function() {
// we'll assume success
this._oldModified.push(this._modified);
return Parent.save.apply(this, arguments);
},
fetch: function() {
this._oldModified.push(this._modified);
return Parent.fetch.apply(this, arguments);
}
});
};
/**
* Connect a "collection" class to CiviCRM's APIv3
*
......
......@@ -5,6 +5,7 @@ var MALFORMED_CONTACT_ID = 'z';
var ContactModel = Backbone.Model.extend({});
CRM.Backbone.extendModel(ContactModel, 'Contact');
CRM.Backbone.trackSaved(ContactModel);
var ContactCollection = Backbone.Collection.extend({
model: ContactModel
......@@ -80,27 +81,33 @@ asyncTest("create/read/delete/read (ok)", function() {
first_name: "George" + TOKEN,
last_name: "Anon" + TOKEN
});
equal(c1.isSaved(), false, "");
// Create the new contact
c1.save({}, {
error: onUnexpectedError,
success: function() {
equal(c1.get("first_name"), "George" + TOKEN, "save() should return new first name");
equal(c1.isSaved(), true, "");
// Fetch the newly created contact
var c2 = new ContactModel({id: c1.get('id')});
equal(c2.isSaved(), true, "");
c2.fetch({
error: onUnexpectedError,
success: function() {
equal(c2.get("first_name"), c1.get("first_name"), "fetch() should return first name");
equal(c2.isSaved(), true, "");
// Destroy the newly created contact
c2.destroy({
error: onUnexpectedError,
success: function() {
equal(c2.isSaved(), true, "");
// Attempt (but fail) to fetch the deleted contact
var c3 = new ContactModel({id: c1.get('id')});
equal(c3.isSaved(), true, "");
c3.fetch({
success: onUnexpectedSuccess,
error: function(model, error) {
......@@ -139,20 +146,27 @@ module('model - update');
asyncTest("update (ok)", function() {
var NICKNAME = "George" + new Date().getTime();
var c = new ContactModel({id: VALID_CONTACT_ID});
c.save({
equal(c.isSaved(), true, "");
c.set({
nick_name: NICKNAME
}, {
});
equal(c.isSaved(), false, "");
c.save({}, {
error: onUnexpectedError,
success: function() {
equal(c.get("nick_name"), NICKNAME, "save() should return new nickname");
var c2 = new ContactModel({id: VALID_CONTACT_ID});
c2.fetch({
error: onUnexpectedError,
success: function() {
equal(c2.get("nick_name"), NICKNAME, "fetch() should return new nickname");
start();
}
_.defer(function(){
equal(c.isSaved(), true, "");
// read back - make sure the save worked
var c2 = new ContactModel({id: VALID_CONTACT_ID});
c2.fetch({
error: onUnexpectedError,
success: function() {
equal(c2.get("nick_name"), NICKNAME, "fetch() should return new nickname");
start();
}
});
});
}
});
......@@ -161,13 +175,19 @@ asyncTest("update (ok)", function() {
asyncTest("update (error)", function() {
var NICKNAME = "George" + new Date().getTime();
var c = new ContactModel({id: VALID_CONTACT_ID});
c.save({
equal(c.isSaved(), true, "");
c.set({
contact_type: 'Not-a.va+lidConta(ype'
}, {
});
equal(c.isSaved(), false, "");
c.save({}, {
success: onUnexpectedSuccess,
error: function(model, error) {
assertApiError(error);
start();
_.defer(function(){
equal(c.isSaved(), false, "");
start();
});
}
});
});
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment