saveChanges( ) method
- Last Updated: January 17, 2024
- 19 minute read
- OpenEdge
- Version 12.8
- Documentation
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
|
- use-submit
- An optional
booleanparameter that whenfalse(or not specified), tellssaveChanges( )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).
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
- 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
booleanthat istrueif all record-change operations invoked on the resource bysaveChanges( )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 thesaveChanges( )method, especially if the callback is registered usingpromise.done( )andpromise.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:
-
"Delete"— All record deletions are applied. -
"Create"— The creation of all new records is applied. -
"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.
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.
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:
- 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: - 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 JSDOafter*event callbacks complete execution. Note that the signatures of all Promise method callbacks match the signature of theafterSaveChangesevent callback function so you can specify an existingafterSaveChangesevent 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 theremove( )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.
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.
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.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.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.
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. - 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
jsrecordorjsrecordsproperty. 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.
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 only — Either callgetErrors( )or inspect theresponseandxhrproperties of the callback request parameter object for any error information. Inspect the callback record parameter or thejsrecordproperty (the record object) of the returned request object for information on operation data.afterSaveChangesevent fired or a promise.fail( )method callback executed after all individual CUD operations on one or more records of data complete — CallgetErrors( )and inspect thebatchproperty of the callback request parameter object to identify and retrieve any additional information and data returned for each CUD operation executed as part of invokingsaveChanges(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 data — Either callgetErrors( )and inspect the callback record parameter or thejsrecordproperty (the record object) of the returned request object for information on the operation data, or inspect theresponseandxhrproperties of the callback request parameter object for any error information. If you inspect theresponseandxhrproperties and no error information is found, inspect the callback record parameter or thejsrecordproperty (the record object) of the returned request object. As part of this inspection, check the return value of thegetErrorString( )method called on the record object; a value other thannullorundefinedreturns an error response for the corresponding before-image CUD operation executed on the server.afterSaveChangesevent fired or apromise.fail( )method callback executed for CUD operations on one or more records of data — CallgetErrors( )and inspect thebatchproperty of the callback request parameter object to identify and retrieve any additional information and data returned for each CUD operation executed as part of invokingsaveChanges(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 operation — Either callgetErrors( )and inspect the callback record parameter or thejsrecordproperty (the record object) of the returned request object for information on the operation data, or inspect theresponseandxhrproperties of the callback request parameter object for any error information on the specific record change. If you inspect theresponseandxhrproperties and no error information is found, inspect the callback record parameter orjsrecordproperty (the record object) of the request object returned. As part of this inspection, check the return value of thegetErrorString( )method called on the record object; a value other thannullorundefinedreturns an error response for this record change as part of the Submit operation executed on the server.afterSaveChangesevent fired or apromise.fail( )method (or equivalent) callback executed for the single Submit operation — Either callgetErrors( )or inspect theresponseandxhrproperties 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 thejsrecordsproperty of the callback request parameter object to identify corresponding record changes and inspect their before-image data.If you inspect the
responseandxhrproperties, and no error information is found, inspect thejsrecordsproperty 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 thegetErrorString( )method called on each record object; a value other thannullorundefinedreturns 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):
|
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. 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:
|
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.
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.
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:
|
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.
getErrors( ) until
the next call to fill( ) or saveChanges( ).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:
|
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.
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:
|
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.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