This is part 4 of a series on drag and drop with Xpages and Dojo.
Apologies for the long delay between part 3 of this series and part 4. In my defense, I was holding out for release 8.5.1 to see if any new functionality was brought to the table that eased Dojo DND development in XPages. I’d also like to acknowledge Paul Withers’ and Declan Lynch’s help in bouncing ideas around and answering some of my more boneheaded XPages questions. Lastly, Jeremy Hodge’s sage advice on the Dojo and XSP script library helped resolve one last thorny issue that was preventing the publication of this tutorial installment.
We ended our last session by properly instrumenting our XPage data table and view controls to serve as Dojo drag and drop containers. After throwing in some Dojo setup code, the containers then receive drag and drop behavioral functionality. Now we’d like to drag and drop between multiple containers. A typical use case would be to drag documents from one view or folder to another. In our example, we’ll be dragging documents from a main view into one that represents an archive. To accomplish this, we embed two containers, in our case data tables attached to views:
So far, we’ve done nothing different than in our previous examples. Now that we’ve got two DND containers to work with though, we’ll be dealing with more complex Javascript so we’ll move most of our code to a script library for code sanity. One neat trick I’ve picked up is to embed an output script block component (new 8.5.1 feature) on the page and create Javascript variables that contain the generated DOM IDs of components we need to reference in the client. In source mode, the script block should look like this:
<xp:scriptBlock id="scriptBlock1"> <xp:this.value><![CDATA[var mainTableId = "#{id:dataTable1}"; var archiveTableId = "#{id:dataTable2}"]]></xp:this.value> </xp:scriptBlock>
Domino will render the EL {id:dataTable1}
as the true DOM ID of the dataTable1
component, which we can use to build our DND containers after the page completes loading. (Hat tip to Matt White via Paul Withers.)
Let’s create a new Javascript code library and create a new function that will take a DOM ID and create a Dojo.dnd source container object from it:
function initDndTable(tid) { var tVar = new dojo.dnd.Source(tid) }//initDndTable
We can then call this function twice from another function:
function initDndTables(){ initDndTable(mainTableId); initDndTable(archiveTableId); }//initDndTables
with the calling parameters being the global table id variables we’ve defined in the script block on the XPage previously.
(You may be wondering why I’m overcomplicating this with separate functions. In truth, at this stage, there’s no need to break out the initialization of the Dojo.dnd containers, but the time will soon come when the need to initialize a specific container will arise.)
Lastly, we kick off the whole process by instructing Dojo to initialize and parse the tables in its onLoad
event thusly:
XSP.addOnLoad(initDndTables);
Notice the use of the XSP object instead of the Dojo (Dojo.addOnLoad) object. The XSP object is IBM’s override of Dojo, with extensions for providing custom XPages functionality. You can browse the uncompressed XSPClientDojo.js file in your NOTESDATAdominojsdojo-1.3.2ibmxspwidgetlayout directory.
Let’s improve the look of our page a bit by styling the containers. To begin with, put a CSS .container selector in your dnd.css file (or any CSS that you’re referencing) and set it to float: left
. You may also have multiple style classes in your StyleClass property, so in addition to having the “container” styleClass for the data tables, add “archive_table” to the styleClass for the second table and set it’s margin-left
property to 50px
to space it a bit horizontally from the first table. This is what the tail of your referenced CSS file should look like:
.container { float: left; } .archive_table { margin-left: 50px; }
I also added a thin black border around the data tables. This is what the two table should look like in your browser:
Done! You now have two data tables associated with Notes views that can interchange rows via drag and drop.
The next hurdle to clear is that we need some way of associating the table rows in the DOM with the backend documents in Notes so that we can perform a drag & drop operation and then instruct the server to update the database with the information reflected in the browser. Let’s do this by appending a column to each of our data tables with hidden HTML fields containing the row documents’ Unique IDs. Note that 8.5.1 has a new hidden field control which we will use in the next iinstallment but that does not seem to work with data inside of repeat controls. So, for the time being, we’ll drop a computed field into our column and use Javascript to emit the HTML for the hidden field:
'<input type="hidden" value="' + mainData.getDocument().getUniversalID() + '">'
MainData is the collection name specified in the Data Table’s main property tab. To complete this operation the field’s “escape” property (in the All Properties tab of the field’s properties view) must be set to false so the HTML is passed through unaltered. Additionally, make sure the field’s name or ID is also blanked out and the field’s “disableTheme” property is set to true. Nulling the field’s ID property and disabling the theme prevents Domino from wrapping the field in a span tag -making it that much easier to programmatically reach for later manipulation.
Now that we have data to allow us to associate the view elements with documents, lets take the process one step further and assign the id of each table row, which is our visual representation of a document, the DOM ID of the document’s UNID. We can create a function that will walk a datatable, extract the ID from the column number containing the IDs and assign the ID value to the row’s ID:
function setUNIDS(tableID,hasTitle,hasTH,UNIDRow){ table1 = dojo.byId(tableID); var childrenIndex = 0; if (hasTitle) childrenIndex++; if (hasTH) childrenIndex++; var tRows = table1.children[childrenIndex].children; //walk rows for (var i=0;i<tRows.length;i++){ UNID = tRows[i].children[UNIDRow - 1].children[0].value; tRows[i].id = UNID; } }//setUNIDs
Of note is that the function params include booleans indicating whether the table has a table header row and whether you’ve enabled the table title by filling in the “caption” property of the table. The function will compute the offset accordingly to access the rows containing actual data. Also note that the UNIDRow
param is not zero based. In our case, the UNIDs are in row 2 of our view, so that will be the value passed to the function.
if we now add our setUNIDs function to the initDndTable
function, we’ll have our table rows’ DOM ID’s set to the documents’ UNIDs for easier manipulation:
function initDndTable(tid) { setUNIDS(tid,true,true,2); var tVar = new dojo.dnd.Source(tid) }//initDndTable
Inspecting the HTML in Firebug will verify that the table row ID’s are now UNIDs:
Get the raw XSP code for our example thus far here and the Javascript library here.
In our next installment we’ll finish up this example by wiring the drop event to a function that collects the data from the event and submits a partial update request to the server to make the Domino application’s data consistent with the browser representation. We’ll finish up with a discussion of a nagging UI issue (can you spot it?) and how to fix it.
Rate me as an author on Scribnia!