Dynamically Modify the Data Source on an Embedded Form in XPages
Managing the Data Source with Scope Variables
I recently worked on an application that had a view of related documents embedded within a parent document page, along with an embedded form to work with the related documents. This concept applies to child/response documents as well as separate documents, related based on common piece of information. (By “embedded form”, I’m referring to a custom control with it’s own data source based on a separate Notes client form, displayed within the context of the parent page.)
As with any type of document data source, creating, viewing, and editing are the three types of data source actions.
By default, the embedded form is blank and can be used to submit a new related document. However, if the user clicks on a related document in the embedded view, they should have the ability to view or edit the selected document, based on their rights.
Along with the action, the document ID needs to be managed. It either needs to be blank (in the case of a new document) or set to the UNID of the document selected in the view.
The following steps were taken to implement this functionality:
- Compute the data source’s action from a scope variable (defaulting to createDocument if no other variable was specified)
- Compute the data source’s documentId from a scope variable
- Set the data source’s ignoreRequestParams attribute to true
- Set the links in the view to set the selected document’s UNID in a scope variable and also determine whether the form should be opened or edited and set the appropriate action in a scope variable (openDocument or editDocument, respectively)
- Set the links in the view to trigger a partial refresh on the form panel in order to update the document in the embedded form
- Set the Save button on the embedded form to clear the embedded form fields after saving a new or edited document
- Hope that no one would try to view and create documents within the same page view
The Biggest Challenge
The biggest challenge I ran into was creating a new document with the embedded form after viewing or editing an existing document. No matter what I did, it would always update the previously-edited document with any changes that were made.
Fortunately, I was able to solve that problem by scoping the data source to the request, so it stopped hanging onto the previous document and allowed for the creation of new documents. This was done via the scope property of the data source, found under All Properties > data > data > dominoDocument > scope.
Managing the Data Source Dynamically
This method works, manipulating the data source directly makes it much simpler to implement and manage! With a few lines of code, you can replace the need for computed data source values, leaving all of the logic in the link that opens the form.
In my case, I’m attaching the data source to a panel on the page. Sven’s snippet shows how to attach the data source to the page itself.
This response from Sven to a question on Stack Overflow shows how you can clear all data sources and add a new one (to work around the same problem I was facing). Note: If you do that, be sure to set the variable name of the new data source the same as when you first bound fields to it, so all field bindings remain in tact.
I did some testing and found that I didn’t even need to remove and re-create the data source. I’ve been testing just updating the parameters of the data source and it seems to work well. One caveat I’ve seen so far is that if I try to open a document in edit mode, close it, then re-open in read mode before changing the documentId of the source to another document, it sticks with the first mode. If you have a case where you need to do that in succession, then maybe removing the data source and re-creating it altogether would do the trick.
Now, I have a ‘New’ button at the top and a repeat control with links to open and edit each document.
var ds = getComponent('panelName').getData(); ds.setDocumentId(''); ds.setAction('createDocument');
Open Document (varRepeat is the repeat control variable)
var ds = getComponent('panelName').getData(); ds.setDocumentId(varRepeat.getDocument().getUniversalID().toString()); ds.setAction('openDocument');
Edit Document (varRepeat is the repeat control variable)
var ds = getComponent('panelName').getData(); ds.setDocumentId(varRepeat.getDocument().getUniversalID().toString()); ds.setAction('editDocument');
Two other things are required for this to work:
- The ignoreRequestParams property of the data source must be set to true, or the embedded form will pick up the id of the parent document from the URL. If you want to be sure it’s set properly, include this line: ds.setIgnoreRequestParams(true);
- Set the button or link to partially-refresh the panel that includes the embedded form and data source
This is much easier to maintain that all of the steps that the other method required.