Over the weekend I uploaded a new release of the XPages Help Application to OpenNTF (now available under the short URL http://xhelp.openntf.org. As well as a few minor changes, the main enhancement was a change to use the dijit.Tree Dojo widget for all navigation throughout the application. This may seem a relatively small piece of work, but actually required a significant change to the Java classes within the application, as well as the custom controls and XPages comprising the application.

The dijit.Tree widget uses a Dojo REST store containing JSON content which is then translated into a hierarchical tree navigation. So the first step was to get the JSON output. To do this, the first step was to look at the examples on the DojoToolkit website and compare the JSON used in the examples there to the tree outputted so I could work out what content I needed from my Java beans. Firebug is useful for this, and it’s not hard to work out the URL required to get to the JSON source. The first thing that strikes one is that each parent object has an item called children which lists the references of all its children. This was what made me realise it was a significant undertaking when I first began developing the application back in April and why the earlier versions had a more simplistic hierarchical navigation, just with a padding relative to the level in the hierarchy it was and a repeat to get children of an entry on the fly. For this, somehow, I needed to store all children against the parent. But for obvious reasons I wanted to get it dynamically. The compromise – at least for the time being – was to calculate the whole hierarchy and store it in an applicationScope variable. Currently I’m not expecting a single instance of the application to hold millions of Help documents, so it’s a problem for another day.

But this solution too meant I wished to minimise the amount of items being stored for each Help document in the JSON data. I cut it down to four – an id, the level, the document’s UNID, and the title. The next step was then to output the JSON. Although an RPC call is probably the optimal method, again, I chose to take smaller steps because I’m leaping into the unknown. My approach, as ever, was get it working, then get it working better. So I just used an XAgent – an XPage with rendered=”false” and outputting JSON. This again gave the benefit of being able to test the JSON and compare whenever it wasn’t working in the store.

I began with the area that used the simplest hierarchical navigation, the index. It’s definitely hierarchical, but only ever with two levels – a category and a link to a Help document. I had my JSON, the next was the data store and the dijit.Tree widget itself. Within the tree I also needed to add an onClick event to open the Help document. Once I had one working, it was just a case of repeating the process for the contents, bookmarks and search – get the JSON looking right, try to generate the tree, then troubleshoot any JSON output issues.

Finally there were two more challenges. The first was adding the tooltip to show debug information. I wanted to retain this functionality. The simplest method was to add the tooltip to the document instead of the navigation, but that didn’t feel acceptable. I didn’t want to store all the debug information in the scoped variables, so I had to get it on the fly. The first step was to add ‘passThru HTML’ to the page using a Computed Field with escape=”false” to add a script tag of type dojo/method to handle the mouseover – this is held in the theme using the themeId Tree.Tooltip. Why take this approach? Because I only want the event to be available if sessionScope.setDebug is true. Then I had to get the content. I had some information in the scoped variable, I had the UNID there, so it was just a case of adding another function – this time a dojo.xhrGet request – to return the tooltip contents. I was very happy when I found my blog post for the format of dijit.showTooltip() and found that a string containing HTML still allowed me to output a multi-line label on the tooltip.

The second was more challenging. Help documents that were also folder-level (so had children) were not displaying when you clicked on them. I searched the web and found other people hitting the same problem with dijit.Tree. The only resolution anyone suggested was to set the openOnClick=”false” on the dijit.Tree. This meant it opened the Help document when you clicked on the title, and forced the user to click on the twistie to expand or collapse. It worked, but I wasn’t happy. So I dived into the JavaScript for dijit.Tree. That expanded and collapsed based on openOnClick set to true, so I just had to reproduce that code on my XPage as client-side JavaScript. Fortunately the onClick event passes two parameters, the item from the REST store and the node widget of the dijit.Tree that displays that item’s content. The item gave me the document UNID I needed to show the document, this time setting a hidden input field and calling a partial refresh. The node widget allowed me to check the isExpandable property to see if it was also a folder and the isExpanded property to check if it was already expanded. Collapsing was easy, just calling the collapse() method on the nodeWidget. Expanding was a little more challenging. The expand() method expanded the node, but nothing was presented. This is because the children needed loading from the store. HUnting through the methods and particularly paying attention to openOnClick (after all, it’s when that’s true that the contents get loaded), I found I needed to additionally call the _onExpandoClick() method of the tree, passing in the node as a parameter. The full code of the function called from onClick is:

  1. function openPage(item,nodeWidget) {
  2.     var id=helpStore.getValue(item,”docId”);
  3.     if (nodeWidget.isExpandable) {
  4.         if (nodeWidget.isExpanded) {
  5.             nodeWidget.collapse();
  6.         } else {
  7.             //Need to call onExpandoClick() on tree, to load contents and expand on node to expand
  8.             nodeWidget.tree._onExpandoClick({node:nodeWidget});
  9.             nodeWidget.expand();
  10.         }
  11.     }
  12.     if (id != “null”) {
  13.         dojo.byId(“#{id:docIdField}”).value=id;
  14.         XSP.partialRefreshPost(“#{id:dynamicContent1}”,{});
  15.     }
  16. };

To coincide with the release I’ve also uploaded a video about the application (the user-interface, not the design) to Intec’s YouTube channel. A copy is embedded below. Feel free to download the application from OpenNTF and have any suggestions for enhancements (or find anything not working), please let me know via the OpenNTF project.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top