Dashboard > DHIS Documentation > ... > The DHIS 2.0 web solution > Standardized Web Organisation Unit Tree
  DHIS Documentation Log In | Sign Up   View a printable version of the current page.  
  Standardized Web Organisation Unit Tree
Added by Torgeir Lorange Østby, last edited by margrsto on Feb 26, 2007  (view change)
Labels: 
(None)

Standarized Web Organisation Unit Tree

Background

Some web modules need a widget for selecting organisation units, and this widget should be standardized in order to give the users a consistent way of selecting organisation units across modules when working with the system. This page presents a possible solution in mixed levels of detail.

Solution

Set/get/clear

Assume a bean with this interface exists:

OrganisationUnitSelectionManager.java
public interface OrganisationUnitSelectionManager
{
    /**
     * Sets the root of the selection tree. If the selected OrganisationUnit
     * is not part of this root's subtree, the selected OrganisationUnit will
     * be cleared.
     */
    void setRootOrganisationUnit( OrganisationUnit organisationUnit );

    /**
     * Returns the root of the selection tree.
     */
    OrganisationUnit getRootOrganisationUnit();

    /**
     * Resets the selection tree to use the actual root of the
     * OrganisationUnit tree.
     */
    void resetRootOrganisationUnit();

    /**
     * Sets the selected OrganisationUnit. The OrganisationUnit must be in
     * the subtree of the selected root to be accepted.
     */
    void setSelectedOrganisationUnit( OrganisationUnit organisationUnit );

    /**
     * Returns the selected OrganisationUnit. The returned OrganisationUnit
     * is always in the subtree of the selected root, or null of no
     * OrganisationUnit is selected.
     */
    OrganisationUnit getSelectedOrganisationUnit();

    /**
     * Clears the selection and makes getSelectedOrganisationUnit() return
     * null.
     */
    void clearSelectedOrganisationUnit();
}

All WebWork Actions requiring an OrganisationUnit to be selected can use this bean to get the selected OrganisationUnit, and possibly use the other methods if necessary.

The module containing this bean will also contain classes for creating and maintaining a Web tree widget. When a user selects an OrganisationUnit using this widget, the corresponding Action in this module is executed, setting the OrganisationUnit using the described bean. After this, the module displaying the main page will be notified via JavaScript that the selected OrganisationUnit has changed.

Creating and maintaining the tree widget

Since an OrganisationUnit tree can contain thousands of OrganisationUnits, lazy loading with Ajax is preferable. But to build the tree using Ajax each and every time the page is reloaded might not be a good solution. So a WebWork interceptor can be used to create the part of the tree that is required for the selected OrganisationUnit to be visible, i.e. all the OrganiaztionUnits in the path of the subtree starting with the selected root, down to the selected OrganisationUnit, also displaying all the siblings to the OrganisationUnits in this path. The rest of the tree can be lazy loaded using Ajax. The interceptor will typically be executed after the Actions have been executed, allowing the Actions to change the parameters of the tree widget before the widged is created by the interceptor. The interceptor can also cache the tree until the parameters are changed, minimizing the amount of method calls to OrganisationStore.

Configuration and use

Assume all the above is put inside a separate Maven project. An xwork.xml file is required to map the interceptor, let's call it org-unit-tree.xml for now. This file must extend the dhis-default.xml file because it needs the transactionStack, and so modules needing the org-unit-tree can include and extend org-unit-tree.xml instead of dhis-default.xml. If one wants the tree widget on a page, they simply add <interceptor-ref name="orgUnitTreeStack"/> to their action mapping, activating the interceptor which will build and add the html to the output value stack, making it available for reference in the Velocity templates. The Velocity templates can simply just use the value, and if it's null, no tree widget is displayed.

One problem is the JavaScript containing the Ajax code etc. If this Maven project is packaged as a

  1. jar file, the JavaScript file has to be distributed among the modules that use this project. If other files, like images etc, also are needed, these must also be distributed.
  2. war file, all the modules using this project must use the web-portal-maven-plugin when building the module.

At the moment, I vote for 1.

Some notes on the client side JavaScript (Hans)

I picture a few different actions:

  • OrgUnitTreeAction - which displays the unit tree
  • OrgUnitSelectorAction - which sets the currently selected OrgUnit.
  • OrgUnitChildrenAction - for incremental downloading of child trees

OrgUnitTreeAction could use OrgUnitTree.vm, which would produce a HTML tree, based on the contents of the bean above.

This VM-file could output something like this:

<ul id="orgUnitTree">
  <li id="org1"><span onclick="selector.selectUnit(1)">Some unit</span>
    ...
  </li>
  ...
</ul>

OrgUnitSelectorAction could use OrgUnitSelector.vm, which outputs (XML) data about the selected unit, for use with Ajax.

As mentioned on the dev list, the module could "listen" to changes in the tree and react accordingly. A possible definiton of such code:

OrgUnitSelector.js (included in OrgUnitTree.vm):

<script type="text/javascript">

/**
 * Class for handling selection of orgUnits.
 */
function OrgUnitSelector () {

  var url = "orgUnitSelector.action";
  var req = null;
  var selectionListeners = [];

  this.addOrgUnitSelectionListener = function ( listener ) {
    if ( listener.orgUnitSelected ) {
      selectionListeners.push(listener);
    }
  }

  /** Click event handler function **/
  this.selectorgUnit = function ( id ) {
    req = getXHR();
    req.onreadystatechange = orgUnitReceived;
    req.send(null);
  }

  /** XHR callback function **/
  function orgUnitReceived() {
    if ( req.readyState == 4 && req.status == 400 ) {
      fireOrgUnitSelected(req.responseText);
    }
  }

  /** Notify listeners of selection **/
  function fireOrgUnitSelected( id ) {

    for ( var i, listener; listener = selectionListeners[DHIS2:i]; i++ ) {
      listener.orgUnitSelected(id);
    }

  }

  function getXHR() {
    return window.XMLHttpRequest ?
      new XMLHttpRequest :
      new ActiveX ( "Microsoft.XMLHttp");

  }

}

selector = new OrgUnitSelector ();

</script>

The module could utilize the code this way (Data entry as an example):

Foo.vm

#include("OrgUnitTree.vm")
<script type="text/javascript">

function orgUnitSelected ( id ) {
  window.open('dataentry.action?unitId=' + id);
}


selector.addOrgUnitSelectionListener(orgUnitSelected);

</script>

Or, the inclusion could/should be done in WP. But the module should be aware of this code, without having to run WP.

Questions

  1. How will the selected unit be stored on the server? Is it enough to set it in some bean instance variable? Would that survive the current session? Alternatively set it in a session object.
  2. What would the selector send back to the client? Just the id, XML data about the unit? With info about children?
  3. What should fireOrgUnitSelected send to the module JS code? The id, XML data, or what?
  4. How would/could/should the module use this data? In the case of Data Entry, the entire page would have to be reloaded, as examplified above, as it'd need to download new datasets, etc. An alternative would be to use Ajax for this too. But what about other modules?
  5. We need some use cases for other modules than DE,

Site powered by a free Open Source Project / Non-profit License (more) of Confluence - the Enterprise wiki.
Learn more or evaluate Confluence for your organisation.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.6 Build:#812 Aug 06, 2007) - Bug/feature request - Contact Administrators