Dienstag, Januar 03, 2006

Tree View AJAX Control

The sample page is available at: http://www.mathertel.de/AjaxEngine/S03_AJAXControls/TreeView.aspx.

Hierarchically structured data can be found in various places on this world. File systems, network structures, dependencies, organisation charts, bill of materials etc. are hierarchical by nature or by design or can be viewed to be so.

(I personally think that the reason behind the fact that so many structures are hierarchically organized is that more complex structures are too chaotic to be understood easily.)

Displaying hierarchical data as trees is relatively easy to be implemented and you can find many implementations with ASP.NET Controls on the web. But when it comes to mass data situations (like the content of a file system) it is almost impossible to load the complete tree at. A simple paging approach, that is known from huge table-data doesn't fit for structured data.

Displaying this kind of data of a good place for using the AJAX technology and the architecture of my AJAX controls is also used here. The best solution is to load and open only the root level of the tree when loading the page and to load all sub-levels when the user wants to dig down by clicking a folder. Already loaded sub-levels can stay at the client until the page gets reloaded.

The AJAX engine can be used to solve the server call in the background very easily by using a WebService that can be queried for sub-level data. With JavaScript and the XMLhttp object this data can be requested from the server an transformed into html code to display all the nested items.

1. Defining a contract

The AJAX control, after beeing loaded will use a WebService for retrieving the list of sub-nodes (folders and files) from a given node (the user clicks). A WebService that can be used for this purpose has a very simple interface with only a single method. The parameter is used with a key that uniquely defines a already known folder node and the return value is a structure with the folders and files under that node.

The unique key of a node can be described by a path string and the return value is typed as a XML document:

[WebMethod]
public XmlDocument GetSubNodes(string path);

Every folder and every file must have a name that must be unique among the subnodes of a folder ans we can concatinate these names as we usually do for filesystems to get a overall unique identifying string.

The returned XML has the form:

<folder>
  <folder name="[name of the folder]" title="[some more text]" />
  <file name="[name of a file]" title="[some more text]" />
</folder>

There can be as multiple files and folders in any order.

2. Implementing a WebService

The WebService implementation we need is very simple. I've published a version that loads a XML file and uses this data source.

3. Javascript and HTML: building a tree layout

There are many <table> - based sample implementations for tree structures on the web. I use a very simple approach here that works fine but supports the indenting of subnodes, but no lines in front of the icons that uses <div>-elements. Here is the basic structure of a folder with the container of the subnodes:

<div class="du" name="foldername"><span class="ft">foldertitle</span></div>
<div class="subframe"></div>

The click event is captured at the root level and has to do the following things:

  • identify the clicked folder object (<div> with class=[do|dc|de|du])
  • hiding (do) or showing (dc) the container with the subnodes
  • or starting an AJAX action (du) that loads the subnodes

4. the AJAX action

The context parameter of the only ajax action we need is the node of the folder with the still unkown subnodes.

The prepare function builds the path string for this folder.

The asynchronous call will return the xml document that is passed to the finish method.

In the finish method we transform the returned xml document into the html wee need by using a simple xslt transformation that can be found inline in the file of the page.

All we have to code for that is an AJAX Action:

  // Retrieve the sub-nodes of a given folder.
  ExploreAction: {
    delay: 10,
    queueMultiple: true,

    prepare:
      function(src) { 
        var path = "";
        var root = jcl.FindBehaviourElement(src, TreeViewBehaviour);
        while ((src != null) && (src != root)) {
          if (src.className == "subframe") {
            src = src.previousSibling;
          } else if (src.className == "do") {
            path = "/" + src.name + path;
            src = src.parentNode;
          }
        }
        while (path.substr(0,2) == "//")
          path = path.substr(1);
        return (path);
      },

    call: "proxies.TreeView.GetSubNodes",

    finish:
     function(data, src) {
       jcl.FindBehaviourElement(src, TreeViewBehaviour).ExtendTree(src, data);
     },
    
    onException: proxies.alertException
  }, // FetchAction

That's still a lot of code, so we build a Control to get that coding off from our daily work by implementing a control.

5. the AJAX control

All the coding up to here should be covered by the AJAX Control so the developer only adds this control with the following attributes:

<ajax:TreeView runat="server" title="Cities of USA" service="WebServiceAlias" />

The title attribute is the label shown on the root folder.

The service attribute is the alias name of the web service that serves the data of the tree

The complete implementation can be found in the 2 files that build the AJAX Control and the JavaScript behaviour: TreeView.ascx and TreeView.js that can be found in the controls folder.

Keine Kommentare: