JSDO properties, methods, and events reference : saveChanges( ) method
  

saveChanges( ) method

Synchronizes to the server all record changes (creates, updates, and deletes) pending in JSDO memory for the current Data Object resource since the last call to the fill( ) or saveChanges( ) method, or since any prior changes have been otherwise accepted or rejected.
This method also causes an offline or online event to fire if it detects that there has been a change in the JSDO login session's online state.
This method always executes asynchronously and returns results (either or both) in subscribed JSDO event callbacks or in callbacks that you register using methods of a Promise object returned as the method value. A Promise object is always returned as the method value if jQuery Promises (or the exact equivalent) are supported in your development environment. Otherwise, this method returns undefined.
Return type: jQuery Promise or undefined
Applies to: progress.data.JSDO class
Working record: After execution, any prior working record settings for the tables of the JSDO are no longer set.

Syntax

saveChanges ( [ use-submit ] )
use-submit
An optional boolean parameter that when false (or not specified), tells saveChanges( ) to individually invoke all pending resource Create, Update, and Delete (CUD) operations, which are sent one record at a time across the network. You can use this option with a JSDO that accesses either a single-table or multi-table resource, with or without before-image support. Results for each record change are returned across the network to the JSDO after the operation on that record completes. The JSDO provides standard mechanisms to handle the results from each standard CUD operation (see the section on returning and handling results, below).
When true, this parameter tells saveChanges( ) to invoke a single Submit operation on the resource that handles any pending record-change (CUD) operations in a single network request. You can use this option only with a JSDO that accesses a single OpenEdge ProDataSet resource that supports before-imaging. (Rollbase resources do not support Submit.)
Note: With either type of operation (CUD or Submit) on an OpenEdge resource, the behavior of the operation on the server depends entirely on the Business Entity that implements the resource. To synchronize JSDO memory appropriately with the resource implementation, you must set the JSDO autoApplyChanges property and call the JSDO accept*Changes( ) and reject*Changes( ) as described later in this topic. The behavior of CUD operations on a Rollbase resource is provided by a standard Rollbase server API and works the same way for any Rollbase resource.

Promise method callback signatures

jQuery Promise objects define methods that register a callback function with a specific signature. The callback signatures depend on the method that returns the Promise. Following are the signatures of callbacks registered by methods in any Promise object that saveChanges( ) returns:
Syntax:
promise.done( function ( jsdo , success , request ) )
promise.fail( function ( jsdo , success , request ) )
promise.always( function ( jsdo , success , request ) )
promise
A reference to the Promise object that is returned as the value of the saveChanges( ) method. For more information on Promises, see the notes on Promises in the description of the progress.data.JSDO class.
jsdo
A reference to the JSDO that invoked the saveChanges( ) method (record-change operations) on its resource. For more information, see the description of the jsdo property of the request object.
success
A boolean that is true if all record-change operations invoked on the resource by saveChanges( ) were successful. For more information, see the description of the success property of the request object.
Note: It is not always necessary to test the value of success in a Promise method callback for the saveChanges( ) method, especially if the callback is registered using promise.done( ) and promise.fail( ), where the callback executes according to this value.
request
A reference to the request object returned after the saveChanges( ) method completed execution and returned all results from its record-change operations on the server. For more information, see the description of the request object.

Default CUD operation execution: one changed record at a time

Without use-submit, the saveChanges( ) method invokes the pending CUD operations in JSDO memory one record at a time across the network, and in the following general order of operation type:
1. "Delete" — All record deletions are applied.
2. "Create" — The creation of all new records is applied.
3. "Update" — Updates are applied to all modified records.
The sending of changes for multiple operations on the same record is optimized so the fewest possible changes are sent to the server. For example, if a record is first updated, then deleted in JSDO memory, only the deletion is sent to the server. However, note that all the changes to the record are applied to JSDO memory in a pending manner in case the deletion fails on the server.
If the JSDO is accessing an OpenEdge ProDataSet resource that is configured to support before-imaging, the JSDO also sends before-image data, along with each changed record, across the network to the server.
Note: Without use-submit, this method performs no batching of resource CUD operations. That is, the Delete operation is invoked over the network for each deleted record, followed by the Create operation for each created record, and finally by the Update operation for each updated record. So, even for a ProDataSet resource that supports before-imaging, each CUD operation executes over the network only one record at a time and cannot be part of a multi-record transaction.

Submit operation execution: multiple changed records at a time

If the JSDO is accessing an OpenEdge ProDataSet resource that supports before-imaging and the Submit operation, with use-submit specified as true, this method sends all pending Delete, Create, and Update record changes to the server in a single Submit operation. This Submit operation can thus manage a single, multi-record server transaction. For more information on the Data Object Submit operation, see Using JSDOs to create mobile and web clients.
In this case, all the record changes, along with the before-image data, are sent in the ProDataSet to the server, to be processed by a single ABL routine that implements the Submit operation. This allows all associated server CUD operations to be part of a single server transaction in which all pending record changes are attempted, with all results returned in the ProDataSet in a single network response, including any error information for each record change.
If you specify use-submit as true for a ProDataSet resource, the implementing Business Entity must be created with before tables for every temp-table that the ProDataSet manages and configured to support both before-image data and invocation of the Submit operation. Otherwise, this method throws an exception.
If you specify use-submit as true for a temp-table resource, this method throws an exception.

Returning and handling results

This method returns results asynchronously in two different ways, and in the following order, depending on the development environment used to build the mobile app:
1. Using JSDO named events for all environments — The following events fire before and after the saveChanges( ) method executes, respectively, with results handled by callback functions that you subscribe as documented for each event:
a. beforeSaveChanges event
b. beforeDelete event
c. afterDelete event
d. beforeCreate event
e. afterCreate event
f. beforeUpdate event
g. afterUpdate event
h. afterSaveChanges event
2. Using a Promise object returned for environments that support jQuery Promises — Any callbacks that you register using Promise object methods all execute both after the saveChanges( ) method itself and after all subscribed JSDO after* event callbacks complete execution. Note that the signatures of all Promise method callbacks match the signature of the afterSaveChanges event callback function so you can specify an existing afterSaveChanges event callback as the callback function that you register using any Promise method.
Because the callbacks that you register with any returned Promise methods execute only after all subscribed after* event callbacks complete, you can invoke logic in registered Promise method callbacks that can modify any processing done by the event callbacks.
Note that your programming requirements for any afterCreate, afterUpdate, afterDelete (CUD operation) event callback, and for any afterSaveChanges event or Promise method callback can be affected by your setting of the JSDO autoApplyChanges property.

Behavior of event and Promise callbacks when autoApplyChanges is true

If you set the autoApplyChanges property to true (the default setting) before you invoke saveChanges(false), and a corresponding record-change (CUD) operation succeeds on the server, the JSDO automatically accepts and synchronizes the change in JSDO memory.
If any CUD operation fails, the operation is automatically undone and the JSDO rejects the change by reverting the applicable record in JSDO memory to its last-saved state. Specifically:
*If a Create operation fails, the record is removed from JSDO memory.
*If an Update operation fails, the record reverts to the state it was in immediately preceding the assign( ) method invocation that led to the failure.
*If a Delete operation fails, the record is restored to JSDO memory in its last-saved state. This state does not reflect any unsaved assign( ) method invocations that may have occurred before the remove( ) call.
When the JSDO synchronizes JSDO memory for CUD operations, it uses any before-image data (if available) in each response from invoking the saveChanges( ) method.
If you invoke a Submit operation (saveChanges(true)) on an OpenEdge ProDataSet resource with before-image support, JSDO memory is automatically updated, with changes accepted or rejected based on the success or failure of each record create, update, and delete included in the Submit operation request. If the Submit returns with a network or server error that prevented the operation from being invoked on the server, all changes in JSDO memory associated with the Submit are rejected.
Note: For a Submit operation on a before-image resource, if you want all such record changes rejected if even one record change returns an error, set autoApplyChanges to false and explicitly reject all record changes based on the returned error condition. For more information on behavior when autoApplyChanges is false, see Behavior of event and Promise callbacks when autoApplyChanges is false in this saveChanges( ) method description.
When autoApplyChanges is true, the JSDO automatically clears any error conditions set for the affected record changes only after the record changes have all been rejected and undone and after all registered after* event and Promise method callbacks have executed. If any callback executes with unsuccessful results, you can call the JSDO getErrors( ) method on a table reference to return any errors from the resource operation or operations associated with the specified JSDO table. For more information, see Error handling in this saveChanges( ) method description.
Note: The errors from CUD or Submit operations that you return using getErrors( ) remain available for return until the next invocation of fill( ) or saveChanges( ), regardless of the autoApplyChanges setting. For more information, see the getErrors( ) method description.
Note: You can manually inspect error conditions and information as part of after* event and promise.fail( ) callback execution when autoApplyChanges is true. However, checking for such error information might not be as useful as when autoApplyChanges is false, because all JSDO memory changes associated with record-change errors are automatically undone.
Caution: Use care when taking any action within event or Promise method callbacks that might interfere with the automatic acceptance or rejection of pending record changes. If you want to manually manage the handling of pending record changes in response to resource operations in these callbacks, consider setting autoApplyChanges to false before invoking saveChanges( ).

Behavior of event and Promise callbacks when autoApplyChanges is false

If you set the autoApplyChanges property to false before you invoke saveChanges( ), you must use one of the following methods to manually accept or reject any record changes in order to synchronize JSDO memory with operation results from the server. Use the method that is appropriate for the resource operation and the JSDO event or Promise method callback where you manage these changes:
*acceptChanges( )
*acceptRowChanges( )
*rejectChanges( )
*rejectRowChanges( )
Depending on the success of the particular resource operation and the JSDO after* event or Promise method callback in which you respond to operation results, you can check returned request object properties to determine what accept*( ) or reject*( ) method to call. If any callback executes with unsuccessful results, you can call the JSDO getErrors( ) method on a table reference to return any errors from the resource operation or operations associated with the specified JSDO table.
Note: The errors from CUD or Submit operations that you return using getErrors( ) remain available for return until the next invocation of fill( ) or saveChanges( ), regardless of the autoApplyChanges setting. For more information, see the getErrors( ) method description.
If you are handling results for one or more record-change (CUD) operations in an afterSaveChanges event callback, or in a Promise method callback, in response to calling saveChanges(false) (without using Submit), you can use the batch property of the request object to evaluate the results of each CUD operation invoked by saveChanges( ).
If you are handling results of a Submit operation (saveChanges(true)) on an OpenEdge ProDataSet resource with before-imaging, in an afterSaveChanges event or Promise method callback you can use the returned jsrecords property of the request object to help evaluate the results of each attempted record-change.
If you want to evaluate the results of any single record-change operation invoked by saveChanges(false) (a CUD operation with or without before-image support), or saveChanges(true) (a Submit operation) with before-image support, you can inspect the request object returned in an appropriate after* event callback that you have registered for each CUD operation, where you can identify the associated record change using the jsrecord property.
Note that the acceptChanges( ) and rejectChanges( ) methods might not be as useful for synchronizing JSDO memory with the server as the corresponding acceptRowChanges( ) and rejectRowChanges( ) methods, which you can invoke selectively in response to the results of individual record-change operations. The acceptChanges( ) and rejectChanges( ) methods are most useful for accepting or rejecting all record changes in JSDO memory based on the results of a single server transaction (invoked using Submit with before-image support) that either succeeds and applies all server record changes or fails if even one record change fails and undoes all server record changes as part of undoing the transaction. For more information, see the reference description for each method.
For more information on handling errors from failed record-change operations, see Error handling in this saveChanges( ) method description.

Error handling

You can use two mechanisms for retrieving information about any failure (error) in the most recently executed saveChanges( ) call and the record changes (with or without before-image support) that might be discarded as a result:
*Call the getErrors( ) method on any or all JSDO table references involved in the operation. For more information, see the getErrors( ) method description.
Note: Updated to support all possible types of error returns in Progress Data Objects 4.3 or later.
*Access properties on the request parameter object returned by an event or Promise method callback that is executed in response to the operation. Use this mechanism to return error information for specific cases, for example, to return related data for an error using the jsrecord or jsrecords property. For more information, see the request object description.
An error has occurred when the success parameter of the callback or the success property of the returned request object is false. (Note that for certain Promise method callbacks, such as those registered by promise.done( ) and especially promise.fail( ) for error handling, there is no need to check the value of the success parameter or success property passed to the callback, because the Promise method callback itself executes based on this value.)
The request object properties available for retrieving error information depend on whether the Data Object resource supports before-image (BI) data and on what after* event or Promise method callback is currently executing for the resource operation, or operations, that you are handling. Note that regardless of resource support for BI data, or the currently executing callback, the getErrors( ) method always returns all error information associated with the most recently invoked CUD or Submit operations invoked as part of the most recent saveChanges( ) call.
Note: These error handling options are most useful when the autoApplyChanges property is false. When autoApplyChanges is true, the method automatically accepts or rejects record changes as previously described for this property setting, and clears all associated error conditions and information either after the final after* event fires and executes its final subscribed callback for a given operation or after the final callback registered by a Promise method executes, whichever one executes last. Note that even after the errors are cleared, the getErrors( ) method continues to return the errors associated with the most recently executed saveChanges( ) call until the next fill( ) or saveChanges( ) call.
Thus, for a resource that:
*Does not support before-image data with individual CUD record-change operations by saveChanges(false) — To retrieve discarded changes and error information returned as part of the response to an:
*after* event fired for each CUD operation request invoked on a single record of data onlyEither call getErrors( ) or inspect the response and xhr properties of the callback request parameter object for any error information. Inspect the callback record parameter or the jsrecord property (the record object) of the returned request object for information on operation data.
*afterSaveChanges event fired or a promise.fail( ) method callback executed after all individual CUD operations on one or more records of data complete — Call getErrors( ) and inspect the batch property of the callback request parameter object to identify and retrieve any additional information and data returned for each CUD operation executed as part of invoking saveChanges(false).
*Supports before-image data with individual CUD record-change operations executed by saveChanges(false) (without using Submit) — To retrieve discarded changes and error information returned as part of a before-image response to an:
*after* event fired for a specific CUD operation on a single record of dataEither call getErrors( ) and inspect the callback record parameter or the jsrecord property (the record object) of the returned request object for information on the operation data, or inspect the response and xhr properties of the callback request parameter object for any error information. If you inspect the response and xhr properties and no error information is found, inspect the callback record parameter or the jsrecord property (the record object) of the returned request object. As part of this inspection, check the return value of the getErrorString( ) method called on the record object; a value other than null or undefined returns an error response for the corresponding before-image CUD operation executed on the server.
*afterSaveChanges event fired or a promise.fail( ) method callback executed for CUD operations on one or more records of data — Call getErrors( ) and inspect the batch property of the callback request parameter object to identify and retrieve any additional information and data returned for each CUD operation executed as part of invoking saveChanges(false).
*Supports before-image data with a single Submit operation on one or more records of data executed by saveChanges(true) — To retrieve discarded changes and error information returned as part of a before-image response from an:
*after* event fired for each record-change (CUD) as part of the single Submit operationEither call getErrors( ) and inspect the callback record parameter or the jsrecord property (the record object) of the returned request object for information on the operation data, or inspect the response and xhr properties of the callback request parameter object for any error information on the specific record change. If you inspect the response and xhr properties and no error information is found, inspect the callback record parameter or jsrecord property (the record object) of the request object returned. As part of this inspection, check the return value of the getErrorString( ) method called on the record object; a value other than null or undefined returns an error response for this record change as part of the Submit operation executed on the server.
*afterSaveChanges event fired or a promise.fail( ) method (or equivalent) callback executed for the single Submit operationEither call getErrors( ) or inspect the response and xhr properties of the callback request parameter object for any error information on the entire Submit operation.
If you call getErrors( ) to return Submit operation errors, using any associated record ID information, you can also search the record objects returned by the jsrecords property of the callback request parameter object to identify corresponding record changes and inspect their before-image data.
If you inspect the response and xhr properties, and no error information is found, inspect the jsrecords property to access all the record objects that were targets of the Submit operation and their corresponding record changes and before-image data. As part of this inspection, check the return value of the getErrorString( ) method called on each record object; a value other than null or undefined returns an error response for this record change as part of the Submit executed on the server.

Examples

The following examples show the calling of saveChanges( ) for various types of Data Object resources, with and without before-image support, and using different callback mechanisms to handle the results.
The following code fragment shows a JSDO created for an OpenEdge ProDataSet resource with before-imaging and its autoApplyChanges property set to false so it does not automatically accept or reject changes to data in JSDO memory after a call to the saveChanges( ) method. Instead, it subscribes a single callback for each of the afterDelete, afterCreate, and afterUpdate, events to determine if changes to any eCustomer table record in JSDO memory should be accepted or rejected based on the success of the corresponding resource operation on the server. To change the data for a record, a jQuery event is defined on an update button to update the corresponding eCustomer record in JSDO memory with the current field values entered in a customer detail form (#custdetail):
Table 32. Example: saveChanges( ) using Submit with before-imaging that accepts/rejects changes based on CUD events
dataSet = new progress.data.JSDO( { name: 'dsCustomerOrder',
autoApplyChanges : false } );
dataSet.eCustomer.subscribe('afterDelete', onAfterCustomerChange, this);
dataSet.eCustomer.subscribe('afterCreate', onAfterCustomerChange, this);
dataSet.eCustomer.subscribe('afterUpdate', onAfterCustomerChange, this);

$('#btnUpdate').bind('click', function(event) {
var jsrecord = dataSet.eCustomer.findById($('#custdetail #id').val());
if (jsrecord) {jsrecord.assign();};
});

// Similar controls might be defined to delete and create eCustomer records...

$('#btnCommit').bind('click', function(event) {
dataSet.saveChanges(true); // Invokes the resource Submit operation
});

function onAfterCustomerChange(jsdo, record, success, request) {
if (success) {
record.acceptRowChanges();
// Perform other actions associated with accepting this record change
}
else
{
record.rejectRowChanges();
// Perform other actions associated with rejecting this record change
}
}
When the button is clicked, the event handler uses the findById( ) method to find the original record with the matching internal record ID (#id) and invokes the assign( ) method on jsrecord with an empty parameter list to update its fields in eCustomer with any new values entered into the form. You might define similar events and controls to delete the eCustomer record or add a new eCustomer record.
A jQuery event also defines a commit button that when clicked invokes the saveChanges( ) method, passing true as the use-submit parameter value, to apply all pending changes in JSDO memory on the server in a single network request. Using this parameter, all pending record deletes, creates, and updates, including before-image data, are sent to the server in a single ProDataSet as input to a Submit operation. This operation processes all the changes for each record delete, create, or update on the server, storing the results in the same ProDataSet, including any errors, for output to the client in a single network response.
After the method completes, and all results have been returned to the client from the server, the appropriate event for each resource Delete, Create, or Update operation fires even though multiple changes can be sent using a single Submit operation. If the operation was successful, the event handler calls acceptRowChanges( ) to accept the eCustomer record change associated with the event in JSDO memory. If the operation was not successful, the event handler calls rejectRowChanges( ) to reject the eCustomer record change. An advantage of using an event to manually accept or reject a record change is that you can perform other actions associated with this particular change, such as creating a local log that describes the change or reports the error.
The following examples show similar processing of results on an OpenEdge resource, first using an afterSaveChanges event callback with and without before-imaging, then using equivalent Promise method callbacks for the resource without before-imaging. The final example shows Promise callback handling of a Rollbase resource. The instantiation of the JSDO login session and the JSDO referenced by myjsdo is assumed.
The following code fragment subscribes the callback function, onAfterSaveChanges, to handle the afterSaveChanges event fired on the JSDO, myjsdo after invoking the Submit operation for an OpenEdge ProDataSet resource with a ttCustomer table resource and before-image support:
Table 33. Example: saveChanges( ) using Submit on an OpenEdge ProDataSet resource and handled using the afterSaveChanges event
/* subscribe to event */
myjsdo.subscribe( 'afterSaveChanges', onAfterSaveChanges );

/* some code that initiates multiple CUD operations before
sending them to the server */
var newrec = myjsdo.add();

. . .

var jsrecord = myjsdo.findById(myid);
if (jsrecord) {jsrecord.remove();};

/* call to saveChanges() using Submit with event handling
and with autoApplyChanges set to true by default */
myjsdo.saveChanges(true);

function onAfterSaveChanges( jsdo , success , request ) {
var lenErrors,
errors,
errorType;

if (success) {
/* all record changes invoked by saveChanges()Submit succeeded */
/* for example, accept all changes and redisplay records in the JSDO table */
jsdo.foreach( function(jsrecord) {
/* reference the record/field as jsrecord.data.<fieldName> . . . */

});
}
else { // success = false
/* handle Submit operation errors where either the server request
or one or more record changes might have failed */
console.log("Operation: Submit");
errors = jsdo.ttCustomer.getErrors();
lenErrors = errors.length;
for (var idxError=0; idxError < lenErrors; idxError++) { /* Each error */
console.log(JSON.stringify(errors[idxError]));
} /* for each error message */
}
};
In the above example, the autoApplyChanges property is set to true by default to allow saveChanges( ) to save or reject all changes automatically based on the success of the Submit operation. If the method completes successfully, the JSDO simply accepts all changes in JSDO memory and re-displays the latest data from the records in the JSDO. If it completes unsuccessfully, the JSDO accepts or rejects each record change in JSDO memory based on the success of each submitted record change after the subscribed event callback has executed.
Note: For a Submit operation, you can determine the success of each record change in the onAfterSaveChanges callback based on the result of calling the JSDO getErrorString( ) method on each record object that you return from the value of the request.jsrecords array property.
Also, if the method completes unsuccessfully, the event callback accesses any server exception information by calling the getErrors( ) method on the single table in the JSDO (jsdo parameter). It concatenates any errors found and logs them to the console, possibly in preparation for having the user re-enter their changes. In this case, the error object returned by getErrors( ) for each error is converted to a string in the form of a JSON object using the standard JSON.stringify( ) method.
Note: These messages are accessible by getErrors( ) until the next call to fill( ) or saveChanges( ).
The following code fragment subscribes the callback function, onAfterSaveChanges, to handle the afterSaveChanges event fired on the JSDO, myjsdo, for an OpenEdge single-table resource, ttCustomer, without before-imaging:
Table 34. Example: saveChanges( ) on an OpenEdge resource without before-imaging using the afterSaveChanges event
/* subscribe to event */
myjsdo.subscribe( 'afterSaveChanges', onAfterSaveChanges );

/* some code that initiates multiple CUD operations before
sending them to the server */
var newrec = myjsdo.add();

. . .

var jsrecord = myjsdo.findById(myid);
if (jsrecord) {jsrecord.remove();};

/* call to saveChanges() and event handling */
myjsdo.autoApplyChanges = false;
myjsdo.saveChanges();

function onAfterSaveChanges( jsdo , success , request ) {

var operations = request.batch.operations,
len = operations.length, /* number of resource operations invoked */
lenErrors,
errors,
errorType;

if (success) {
/* all resource operations invoked by saveChanges() succeeded */
/* for example, redisplay records in the JSDO table */
jsdo.acceptChanges();
jsdo.foreach( function(jsrecord) {
/* reference the record/field as jsrecord.data.<fieldName> . . . */

});
}
else {
/* one or more resource operations invoked by saveChanges() failed */
/* handle all operation error messages */
errors = jsdo.ttCustomer.getErrors();
lenErrors = errors.length;
for (var idxError=0; idxError < lenErrors; idxError++) {
switch(errors[idxError].type) {
case progress.data.JSDO.DATA_ERROR:
errorType = "Server Data Error: ";
break;
case progress.data.JSDO.RETVAL:
errorType = "Server App Return Value: ";
break;
case progress.data.JSDO.APP_ERROR:
errorType = "Server App Error #"
+ errors[idxError].errorNum + ": ";
break;
case progress.data.JSDO.ERROR:
errorType = "Server General Error: ";
break;
case default:
errorType = null; // Unexpected errorType value
break;
}
if (errorType) { /* log all error text
console.log("ERROR: " + errorType + errors[idxError].error);
if (errors[idxError].id) { /* error with record object */
console.log("RECORD ID: " + errors[idxError].id);
/* possibly log the data values for record with this ID */
}
if (errors[idxError].responseText) {
console.log("HTTP FULL TEXT: "
+ errors[idxError].responseText);
}
}
else { /* unexpected errorType */
console.log("UNEXPECTED ERROR TYPE: "
+ errors[idxError].type);
}
} /* for each error message */

/* accept or reject record changes based on each operation success */
for(var idx = 0; idx < len; idx++) {
var operationEntry = operations[idx];
if (operationEntry.success) {
operationEntry.jsrecord.acceptRowChanges();
}
else {
operationEntry.jsrecord.rejectRowChanges();
}
} /*for each CUD operation */

}
};
In the above example, the autoApplyChanges property is dynamically set to false prior to calling saveChanges( ) to allow custom handling of the results. If the method completes successfully, the callback simply accepts all changes in JSDO memory and re-displays the latest data from the records in the JSDO.
If the method completes unsuccessfully, the callback logs the error messages (jsdo.ttCustomer.getErrors()) returned for all the operations invoked by saveChanges( ) that failed. In this case, the property values from each error object returned by getErrors( ) are formatted and logged based on the error type for a more readable and informative output than a simple string representation of the JSON object. Finally, it accesses the batch property of the request object returned for the afterSaveChanges event in order to inspect the request object for each resource operation (operations[idx]) that was invoked by saveChanges( ). It then accepts or rejects the record changes for each operation record (operationEntry.jsrecord), depending on the operation success.
Note: These messages are accessible by getErrors( ) until the next call to fill( ) or saveChanges( ).
Note: For a Delete operation error, no data is available in operationEntry.jsrecord because the operation record has already been cleared from JSDO local memory. However, after executing operationEntry.jsrecord.rejectRowChanges( ) to undo the operation on the JSDO, the record is restored to JSDO local memory as expected.
The following code fragment registers done( ) and fail( ) method callbacks on the Promise returned by a saveChanges( ) call on a JSDO, myjsdo, which provides identical handling of operation results for the same OpenEdge resource handled in the previous example using the afterSaveChanges event callback:
Table 35. Example: saveChanges( ) on an OpenEdge resource using a Promise object
/* some code that initiates multiple CUD operations before
sending them to the server */
var newrec = myjsdo.add();

. . .

var jsrecord = myjsdo.findById(myid);
if (jsrecord) {jsrecord.remove();};

/* call to saveChanges() with inline-coded Promise callback handling */
myjsdo.autoApplyChanges = false;
myjsdo.saveChanges().done(
function( jsdo, success, request ) {
/* all resource operations invoked by saveChanges() succeeded */
/* for example, redisplay records in the JSDO table */
jsdo.acceptChanges();
jsdo.foreach( function(jsrecord) {
/* reference the record/field as jsrecord.data.<field-ref> . . . */

});
}).fail(
function( jsdo, success, request ) {
/* one or more resource operations invoked by saveChanges() failed */
var operations = request.batch.operations,
len = operations.length, /* number of resource operations invoked */
lenErrors,
errors,
errorType;

/* handle all operation error messages */
errors = jsdo.ttCustomer.getErrors();
lenErrors = errors.length;
for (var idxError=0; idxError < lenErrors; idxError++) {
switch(errors[idxError].type) {
case progress.data.JSDO.DATA_ERROR:
errorType = "Server Data Error: ";
break;
case progress.data.JSDO.RETVAL:
errorType = "Server App Return Value: ";
break;
case progress.data.JSDO.APP_ERROR:
errorType = "Server App Error #"
+ errors[idxError].errorNum + ": ";
break;
case progress.data.JSDO.ERROR:
errorType = "Server General Error: ";
break;
case default:
errorType = null; // Unexpected errorType value
break;
}
if (errorType) { /* log all error text
console.log("ERROR: " + errorType + errors[idxError].error);
if (errors[idxError].id) { /* error with record object */
console.log("RECORD ID: " + errors[idxError].id);
/* possibly log the data values for record with this ID */
}
if (errors[idxError].responseText) {
console.log("HTTP FULL TEXT: "
+ errors[idxError].responseText);
}
}
else { /* unexpected errorType */
console.log("UNEXPECTED ERROR TYPE: "
+ errors[idxError].type);
}
} /* for each error message */

/* accept or reject record changes based on each operation success */
for(var idx = 0; idx < len; idx++) {
var operationEntry = operations[idx];
if (operationEntry.success) {
operationEntry.jsrecord.acceptRowChanges();
}
else {
operationEntry.jsrecord.rejectRowChanges();
}
} /*for each CUD operation */

});
In the above example, the same processing occurs for successful and unsuccessful execution of saveChanges( ), but there is no need to test the success parameter of the done( ) and fail( ) Promise method callbacks, because they each execute in response to this value.
Note: For a Delete operation error, no data is available in operationEntry.jsrecord because the operation record has already been cleared from JSDO local memory. However, after executing operationEntry.jsrecord.rejectRowChanges( ) to undo the operation on the JSDO, the record is restored to JSDO local memory as expected.
However, if your code already uses the afterSaveChanges event implementation, and you want to quickly change it to use Promises, you can simply register the original onAfterSaveChanges function as the callback for the always( ) Promise method, as in the following code fragment:
Table 36. Example: saveChanges( ) using a Promise object that registers an afterSaveChanges callback

/* some code that initiates multiple CUD operations before
sending them to the server */
var newrec = myjsdo.add();

. . .

var jsrecord = myjsdo.findById(myid);
if (jsrecord) {jsrecord.remove();};

/* call to saveChanges() with Promise callback handling using
the original afterSaveChanges event handler */
myjsdo.autoApplyChanges = false;
myjsdo.saveChanges().always(onAfterSaveChanges);

function onAfterSaveChanges( jsdo , success , request ) {

var operations = request.batch.operations,
len = operations.length, /* number of resource operations invoked */
lenErrors,
errors,
errorType;

if (success) {
/* all resource operations invoked by saveChanges() succeeded */
/* for example, redisplay records in the JSDO table */
jsdo.acceptChanges();
jsdo.foreach( function(jsrecord) {
/* reference the record/field as jsrecord.data.<fieldName> . . . */

});
}
else {
/* one or more resource operations invoked by saveChanges() failed */
/* handle all operation error messages */
errors = jsdo.ttCustomer.getErrors();
lenErrors = errors.length;
for (var idxError=0; idxError < lenErrors; idxError++) {
switch(errors[idxError].type) {
case progress.data.JSDO.DATA_ERROR:
errorType = "Server Data Error: ";
break;
case progress.data.JSDO.RETVAL:
errorType = "Server App Return Value: ";
break;
case progress.data.JSDO.APP_ERROR:
errorType = "Server App Error #"
+ errors[idxError].errorNum + ": ";
break;
case progress.data.JSDO.ERROR:
errorType = "Server General Error: ";
break;
case default:
errorType = null; // Unexpected errorType value
break;
}
if (errorType) { /* log all error text
console.log("ERROR: " + errorType + errors[idxError].error);
if (errors[idxError].id) { /* error with record object */
console.log("RECORD ID: " + errors[idxError].id);
/* possibly log the data values for record with this ID */
}
if (errors[idxError].responseText) {
console.log("HTTP FULL TEXT: "
+ errors[idxError].responseText);
}
}
else { /* unexpected errorType */
console.log("UNEXPECTED ERROR TYPE: "
+ errors[idxError].type);
}
} /* for each error message */

/* accept or reject record changes based on each operation success */
for(var idx = 0; idx < len; idx++) {
var operationEntry = operations[idx];
if (operationEntry.success) {
operationEntry.jsrecord.acceptRowChanges();
}
else {
operationEntry.jsrecord.rejectRowChanges();
}
} /*for each CUD operation */

}
};
Note: For a Delete operation error, no data is available in operationEntry.jsrecord because the operation record has already been cleared from JSDO local memory. However, after executing operationEntry.jsrecord.rejectRowChanges( ) to undo the operation on the JSDO, the record is restored to JSDO local memory as expected.
Finally, the following code fragment registers done( ) and fail( ) method callbacks on the Promise returned by a saveChanges( ) call on a JSDO, myjsdo, that is already instantiated for a Rollbase object resource:
Table 37. Example: saveChanges( ) on a Rollbase resource using a Promise object
/* some code that initiates multiple CUD operations before
sending them to the server */
var newrec = myjsdo.add();

. . .

var jsrecord = myjsdo.findById(myid);
if (jsrecord) {jsrecord.remove();};

/* call to saveChanges() with Promise callback handling */
myjsdo.autoApplyChanges = false;
myjsdo.saveChanges().done(
function( jsdo, success, request ) {
/* all resource operations invoked by saveChanges() succeeded */
/* for example, redisplay records in the JSDO table */
jsdo.acceptChanges();
jsdo.foreach( function(jsrecord) {
/* reference the record/field as jsrecord.data.<field-ref> . . . */

});
}).fail(
function( jsdo, success, request ) {
/* one or more resource operations invoked by saveChanges() failed */
var operations = request.batch.operations,
len = operations.length, /* number of resource operations invoked */
lenErrors,
errors,
errorType;

/* handle all operation error messages */
errors = jsdo.ttCustomer.getErrors();
lenErrors = errors.length;
for (var idxError=0; idxError < lenErrors; idxError++) {
switch(errors[idxError].type) {
case progress.data.JSDO.ERROR:
errorType = "Rollbase Error: ";
break;
case default:
errorType = null; // Unexpected errorType value
break;
}
if (errorType) { /* log all error text
console.log("ERROR: " + errorType + errors[idxError].error);
if (errors[idxError].id) { /* error with record object */
console.log("RECORD ID: " + errors[idxError].id);
/* possibly log the data values for record with this ID */
}
if (errors[idxError].responseText) {
console.log("HTTP FULL TEXT: "
+ errors[idxError].responseText);
}
}
else { /* unexpected errorType */
console.log("UNEXPECTED ERROR TYPE: "
+ errors[idxError].type);
}
} /* for each error message */

/* accept or reject record changes based on each operation success */
for(var idx = 0; idx < len; idx++) {
var operationEntry = operations[idx];
if (operationEntry.success) {
operationEntry.jsrecord.acceptRowChanges();
}
else {
operationEntry.jsrecord.rejectRowChanges();
}
} /*for each CUD operation */

});
For a Rollbase resource, any error results from a resource CUD operation are returned by getErrors( ) as error type progress.data.JSDO.ERROR with the errors[idxError].error property set to the returned string value. This includes results from both CUD operation errors and server errors.
Note: For a Delete operation error, no data is available in operationEntry.jsrecord because the operation record has already been cleared from JSDO local memory. However, after executing operationEntry.jsrecord.rejectRowChanges( ) to undo the operation on the JSDO, the record is restored to JSDO local memory as expected.

See also:

acceptChanges( ) method, acceptRowChanges( ) method, autoApplyChanges property, batch property, data property, fill( ) method, getErrors( ) method, getErrorString( ) method, invocation method, jsrecord property, jsrecords property, rejectChanges( ) method, rejectRowChanges( ) method, response property, success property