Gridx in XPages – 31: Saving Inline Edits
In the last post, I showed how to enable inling editing on a grid column. However, the changes made were only stored in the in-memory data store. In this post, I’ll show how to save the updated value to the back-end document.
Gridx Series
Gridx in XPages — Entire Series
General Concepts
The process will run after the change has been made. The changes will be saved asynchronously so the user isn’t blocked while waiting for the update to happen on the server.
We need a server-side method of applying the changes. In this example, I’ll be using the Domino Data Service (DDS) because it’s a little simpler. You could also create your own custom REST service to receive requests and process the changes.
Domino Data Service
DDS is a REST API that allows you to access databases on a Domino server. It can handle HTTP/S requests and JSON data.
You can use it to get database, view, folder, or document info as well as send updates. We’ll use it to save changes made in the grid.
(If your administrator won’t allow this, then you can go the route of custom REST services)
1. Enabling Domino Data Service on the Server
DDS can be enabled on the Server document (Internet Protocols tab > Domino Web Engine subtab, under the Domino Access Services section. Select Data
in the Enabled Services
field), but better to do on a Web Site document.
Set Enabled Services
to Data
under Domino Access Services
and ensure that PATCH
is selected under Allowed Methods
(on the Configuration tab)
Note: The server document must be set to use web site documents.
2. Enabling Domino Data Service on the Database
You also need to enable it on the specific database (and views, if getting data from views, but we won’t be doing it in this example, so we only need it at the database level.)
HTTP PATCH
If you’re using XPages, then you’re most likely familiar with the HTTP GET
and POST
methods. You may assume that we’d use a POST
to update a document (because that’s what happens when a form is saved), but in this case, we’ll use PATCH
, because it’s more efficient.
A POST
will replace the entire document with the contents that you pass whereas a PATCH will only update the specified fields in the specified document.
We need to determine the URL to which the request will be sent. In general, you’ll use a URL that’s relative to your current path. If you’re updating a document in the same database that’s currently open in the browser, then the URL to send a document update request against DDS looks like this:
/api/data/documents/unid/<documentUNID>
(Many thanks to Marky Roden for his posts with PATCH examples.)
Testing the Configuration
In order to make sure that everything is configured properly, this is a good point to test it out with a hard-coded request.
To do so, go into your database and get the UNID of a document. Also, make note of a field name to modify. Then we can use an XHR
request in the browser console to test running a PATCH
in order to make sure that it works (before adding the complexity of running it from the grid).
dojo.request.xhr("api/data/documents/unid/4030FB42B37B397785257D87004680BC", { data: '{"firstname":"Patched!"}', method: "PATCH", handleAs: "json", headers: {'Content-Type': 'application/json'} }).then(function(data){ console.log('successful'); }, function(err){ console.log('error'); });
If it works, you’ll see the PATCH
request in the browser console (or Net tab) of the browser’s dev tools:
Notes:
- JSON requires double-quotes for properties and values
- The data passed must be a string, so use single quotes to surround the JSON
- handleAs must be set to “json” and the Content-Type header is also required
- The newer Dojo xhr module is not automatically available in XPages, so you need to add it as a Dojo Module Resource
<xp:this.resources> <xp:dojoModule name="dojo.request.xhr"></xp:dojoModule> </xp:this.resources>
Saving Edits in the Grid
Now that we know the server and database are configured to allow us to save changes, we can update the grid to send the proper requests.
To do so, we’ll add an event handler to the onApply
event of the Edit
module. This will fire whenever the user makes a change (and does not cancel it by hitting the ESC key).
The first argument that the onApply
callback receives is an object that gives you access to the row, column, updated value, etc. This gives us what we need to build the proper PATCH request.
To attach a callback to the onApply event, update the grid’s modules
property to include the Edit
module as an object and define the onApply
event callback:
{ moduleClass: Edit, onApply: saveChange }
Here’s the function that runs when a grid cell is changed and sends the PATCH
request to save the changes:
function saveChange(gridObject) { var newValue = gridObject.rawData(); var fieldName = gridObject.column.field(); var rowUNID = gridObject.row.item()['@unid']; // Build set up the JSON object for the update var data = {}; data[fieldName] = newValue; var jsonData = JSON.stringify(data); dojo.request.xhr("api/data/documents/unid/" + rowUNID, { data: jsonData, method: "PATCH", handleAs: "json", headers: {'Content-Type': 'application/json'} }).then(function(data){ console.log('successfully updated document (' + rowUNID + ')'); }, function(err){ console.error('error updating document (' + rowUNID + ')'); }); }
Line 2 gets the updated cell value from the grid (after any fromEditor
processing is done)
Line 3 gets the name of the source field from the column. This will only work if your grid column is named after a single field. If it has a different name, then you’ll need to take that into account when sending the update.
Line 4 gets the UNID of the document from the data store. The item() method of the row provides the entire record for that row, so I’m pulling the @unid property that is provided by the REST service that supplies the data for the grid. (XPages REST services will automatically include it.)
Lines 7-9 set up an object with the data to send to update the back end document and then ensure that it’s formatted as JSON.
Lines 11-20 send the PATCH
request to the server to update the back end document.
Only Send Changes if Data is Modified
The last thing I want to do now is prevent this from firing every time a cell goes out of edit mode. If the user didn’t change the data, then it’s a waste of bandwidth and server processing to send updates to the back-end document.
I don’t see a way to get the original value from the onApply callback, but you can use the onBegin event to store the value in a global variable, then check it before sending any changes.
Here’s a function I added to the onBegin callback:
function beforeEdit(gridObject) { window.OriginalValue = gridObject.rawData(); }
Here’s the module inclusion that adds the callback:
{ moduleClass: Edit, onApply: saveChange, onBegin: beforeEdit }
Now, the first few lines of saveChange() can be updated to check whether the value changed and cut out early if it hasn’t:
// Get the updated value (after any fromEditor callback is done) var newValue = gridObject.rawData(); if (window.OriginalValue == newValue) { return; }
Security
In case you’re wondering, security is based on your current Domino session. If you’re logged into the server, then any PATCH request will be sent with your ID. (If you’re not logged in, it’ll be sent as Anonymous.)
If the user does not have rights to edit the document in the NSF, then the PATCH request will return an error like this:
3 responses to “Gridx in XPages – 31: Saving Inline Edits”
Trackbacks / Pingbacks
- March 9, 2015 -
Great series Brad ¡, thanks for sharing ¡¡
regards ¡¡
For anyone wondering why Brad used PATCH as opposed to PUT, the HTTP methods don’t literally translate to pure CRUD operations. PATCH is meant for existing entry update only, as opposed to a PUT which can be used to update existing or (though I hadn’t run into it) create new (if the queried resource doesn’t exist). I think the ultimate key is how the server handles the request, which can vary.
The docs for the 8.5.3 UP1 DDS API says that (against a Document), PUT will replace all items in a Document while the PATCH request will update selected items on a Document (only). In other words, Brad was right on using PATCH, assuming there are more fields than reflected in the grid.
http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
http://infolib.lotus.com/resources/domino/8.5.3/doc/designer_up1/en_us/DominoDataService.html#ddapi_ref_document_patch
Great series Brad.