asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search








Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Advanced UI Design Using XML and XSL - Part 3: Folder Tree Administration
By Joe Slovinski
Rating: 4.5 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    Welcome to Advanced UI Design Using XML and XSL. Each article in my series will demonstrate the creation of a specific user interface (UI) component using eXtensible Markup Language (XML) and eXtensible Stylesheet Language (XSL). This article expands on a previous article in this series, Folder Tree Creation (see http://www.15seconds.com/issue/010921.htm). The folder tree discussed in this article illustrates how to insert, modify, delete, and rename items within the tree. All operations are performed on the client, thus reducing the load on your server. The client used in this article is Internet Explorer 5.5+. The actual folder tree demonstrated is available in the Download section below.

    Folder Tree Updates

    Since the last article, I have made the following updates.

    1. Automated IDs
    2. Unlimited Metadata Support
    3. Insert/Update Capability
    4. Rename Capability
    5. Delete Capability

    Automated IDs

    Each entity within our tree requires a unique ID. In the previous article the task of assigning these ID's was left to the user. In this article I have automated this task at runtime.

    In this version of the folder tree, unique IDs are assigned via an XSL transformation immediately before rendering the tree.

    
    <xsl:stylesheet version="1.1" 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
      xmlns:dt="urn:schemas-microsoft-com:datatypes">
      
      <xsl:template match="/tree">
        <xsl:element name="tree">
          <xsl:apply-templates select="entity"/>
        </xsl:element>
      </xsl:template>
    
      <xsl:template match="entity">
        <xsl:element name="entity">
          <xsl:attribute name="id">
            <xsl:value-of select="generate-id(.)"/>
          </xsl:attribute>
          <xsl:for-each select="*">
            <xsl:if test="name() = 'contents'">
                <xsl:element name="contents">
                <xsl:apply-templates select="entity"/>
              </xsl:element>
            </xsl:if>
            <xsl:if test="name() != 'contents'">
                <xsl:element name="{name()}">
                <xsl:value-of select="."/>
              </xsl:element>
            </xsl:if>
          </xsl:for-each>
        </xsl:element>
      </xsl:template>
    </xsl:stylesheet>
    
    
    The style sheet traverses our tree.xml file and uses the generate-id() method to assign a unique ID to each individual entity.

    We should note that if this were an n-tier application and our entities were being pulled out of a database, we would not need this process because each entity representing a row in our database would already have a unique identity field.

    Unlimited Metadata Support

    All tree operations allow for descriptive metadata specific to your requirements. This means elements of any name and value can be added to describe the entity.

    For example, below is customer XML from the last article. Also, you'll notice the difference between the two is the customer-specific metadata, such as contact, address and phone. My folder tree will automatically read and interpret this new metadata. Also note that due to "image" being a reserved word I was required to change the "image" entities name to "imageBase".

    
    <entity id="e2">
      <description>Microsoft</description>
      <imageBase>images/book.gif</imageBase>
      <imageOpen>images/bookOpen.gif</imageOpen>
      <onClick>displayCustomer(12345)</onClick>
      <onContextMenu>context/contextCustomer.xml</onContextMenu>
      <contents>
      </contents>
    </entity>
    
    
    Below is sample customer XML from this article.
    
    <entity id="e2">
      <description>Microsoft</description>
      <imageBase>images/book.gif</imageBase>
      <imageOpen>images/bookOpen.gif</imageOpen>
      <onClick>displayCustomer(12345)</onClick>
      <onContextMenu>context/contextCustomer.xml</onContextMenu>
      <contact>Bill Gates</contact>
      <address1>1234 Microsoft Dr.</address>
      <address2>Suite 123</address>
      <city>Microsoft City</city>
      <state>MS</state>
      <zip>12345</zip>
      <phone>(123)132-1234</phone>
      <contents>
      </contents>
    </entity>
    
    
    You'll notice the difference between the two is the customer-specific metadata, such as contact, address, and phone. My folder tree will automatically read and interpret this new metadata.

    With the current folder tree architecture, child entities inherit from their parents. For instance, when you right-click on the Customer Folder and select Insert, you will get a child with all the metadata that the Customer Folder has.

    Insert/Update Capability

    Inserting and updating are very similar because both operations deal with the exact same set of data and use the exact same form. The exceptions are an insert form initializing blank, meaning with empty input elements, and an update form initializing with values. Because of these similarities I was able to combine both insert and update functionality in one XSLT style sheet and one display function, and then separate operational calls

    Insert Update XSLT Forms

    The actual XSLT style sheet that creates our forms follows.

    
    <xsl:stylesheet version="1.1" 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
      xmlns:dt="urn:schemas-microsoft-com:datatypes">
      <xsl:param name="action"/>
      <xsl:param name="selectedEntity"/>
    
      <xsl:template match="entity">
        <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="1" WIDTH="100%">
          <xsl:for-each select="*">
            <xsl:if test="name() != 'contents'">
              <TR>
                <TD CLASS="dataLabel" STYLE="border-right: 1px solid black; 
    border-bottom: 1px solid black;">
                  <xsl:value-of select="name()"/>
                </TD>
                <TD STYLE="border-right: 1px solid black; border-bottom: 1px solid black;" WIDTH="100%">
                  <INPUT CLASS="dataInput" 
    ONFOCUS="document.body.onselectstart = null" 
    ONBLUR="document.body.onselectstart = returnFalse;">
                    <xsl:attribute name="NAME">
                      <xsl:value-of select="name()"/>
                    </xsl:attribute>
                      <xsl:if test="$action = 'update'">
                        <xsl:attribute name="VALUE">
                          <xsl:value-of select="."/>
                        </xsl:attribute>
                      </xsl:if>
                  </INPUT>
                </TD>
              </TR>
            </xsl:if>
          </xsl:for-each>
          <TR>
            <TD STYLE="padding-right: 0px;"/>
            <TD STYLE="padding-top: 0px;padding-left: 0px;">
              <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">
                <TR>
                  <TD>
                    <INPUT TYPE="button" CLASS="buttonOff" NAME="Save" VALUE="Save" ONFOCUS="this.blur();" 
    ONMOUSEOVER="swapClass(this, 'buttonOver')" ONMOUSEOUT="swapClass(this, 'buttonOff')">
                      <xsl:attribute name="ONCLICK">
    <xsl:value-of select="$action"/>Entity('<xsl:value-of select="$selectedEntity"/>')
    </xsl:attribute>
                    </INPUT>
                  </TD>
                  <TD STYLE="padding-left: 2px;">
                    <INPUT TYPE="button" CLASS="buttonOff" NAME="Cancel" VALUE="Cancel" 
    ONFOCUS="this.blur();" ONMOUSEOVER="swapClass(this, 'buttonOver')" 
    ONMOUSEOUT="swapClass(this, 'buttonOff')" ONCLICK="content.innerHTML = '';"/>
                  </TD>
                </TR>
              </TABLE>
            </TD>
          </TR>
        </TABLE>
      </xsl:template>
    </xsl:stylesheet>
    
    
    This XSLT style sheet accepts two parameters called "action" and "selectedEntity". Valid values for the action parameter are "insert" and "update". The selectedEntity parameter takes an entity ID value.


    Figure 1. Resulting XSLT Transformation

    Insert/Update Display Function

    This function receives an action parameter of value "insert" or "update." It then uses the selectedEntity variable to figure out which entity the insert or update will be performed on. It uses the above XSLT style sheet to render the appropriate form.

    
    function insertUpdateDisplay(action) {
      var xslDoc
      var xslTemplate;
      var xslProc;
      var entity;
    
      xslDoc = new ActiveXObject('MSXML2.FreeThreadedDOMDocument')
      xslDoc.async = false;
    
      xslTemplate = new ActiveXObject('MSXML2.XSLTemplate')
    
      xslDoc.load("admin/insertUpdate.xslt");
      xslTemplate.stylesheet = xslDoc;
      xslProc = xslTemplate.createProcessor();
      entity = xmlDoc.documentElement.selectSingleNode("//entity[@id='" + selectedEntity +"']");
      xslProc.input = entity;
    
      xslProc.addParameter("action", action);
      xslProc.addParameter("selectedEntity", selectedEntity);
    
      xslProc.transform();
      
      content.innerHTML = xslProc.output;
    }
    
    
    Insert Operation

    This function receives the entity ID of the "parent to be." The function then creates a replica of the parent entity. Once the child is created, it is appended to the parent's contents collection.

    
    function insertEntity(parentEntityID) {
      var entity;
      var newEntity;
      var element;
      var attribute;
      var xslDoc;
      var i;
    
      xslDoc = new ActiveXObject('MSXML2.FreeThreadedDOMDocument')
      xslDoc.async = false;
      
      xslDoc.load("admin/tree.xslt");
    
      entity = xmlDoc.documentElement.selectSingleNode("//entity[@id='" + parentEntityID +"']");
      newEntity = xmlDoc.createElement("entity");
      attribute = xmlDoc.createAttribute("id");
      attribute.text = document.uniqueID;
      newEntity.attributes.setNamedItem(attribute);
    
      for(i=0; i < entity.childNodes.length; i++) {
        element = xmlDoc.createElement(entity.childNodes(i).baseName);
        if(entity.childNodes(i).baseName != "contents") {
          element.text = eval(entity.childNodes(i).baseName + ".value")
        }
        newEntity.appendChild(element)
      }
      entity.selectSingleNode("contents").appendChild(newEntity);
      document.all[parentEntityID].insertAdjacentHTML("beforeEnd", newEntity.transformNode(xslDoc));
      document.all[parentEntityID].lastChild.style.display = "block";
    
      if(document.all[parentEntityID].open == "false") {
        document.all[parentEntityID].onclick();
      }
      saveXML();
    }
    
    
    Update Operation

    This function receives the ID of the entity that needs updating. The function then loops through the entities while updating values pulled from the current form. Once the entity XML is updated, it is retransformed and swapped with the existing node in the browser DOM.

    
    function updateEntity(entityID) {
      var entity;
      var xslDoc;
      var container;
      var i;
    
      xslDoc = new ActiveXObject('MSXML2.FreeThreadedDOMDocument')
      xslDoc.async = false;
      
      xslDoc.load("admin/tree.xslt");
    
      entity = xmlDoc.documentElement.selectSingleNode("//entity[@id='" + entityID +"']");
    
      for(i=0; i < entity.childNodes.length; i++) {
        if(entity.childNodes(i).baseName != "contents") {
          entity.childNodes(i).text = eval(entity.childNodes(i).baseName + ".value")
        }
      }
      container = document.createElement("DIV");
      container.innerHTML = entity.transformNode(xslDoc)
      
      container.childNodes(0).style.display = "block";
      document.all[entityID].swapNode(container.childNodes(0));
      container = null;
      saveXML();
    }
    
    

    Rename Capability

    When a user selects the rename option of an entity the renameEntityBegin() function is called. It then uses the selectedEntity variable to determine which entity is to be renamed.

    The method first gains a reference called "name" to the browser object that displays the current name of the entity. Once this reference is established, we then set the contentEditable property to true. This allows the user to freely type within this space.

    We also set the focus to this element, which moves the cursor to the beginning of the name. After setting several events that restrict keystrokes and allow selecting of text, the user is ready to rename the entity.

    
    function renameEntityBegin() {
      var name;
      
      name = document.all[selectedEntity + "name"]
      name.contentEditable = true;
      name.focus();
      name.style.cursor = "text";
      name.onkeypress = function anonymous() { renameKeyPress(selectedEntity) };
      name.onblur = function anonymous() { renameEntityEnd(selectedEntity) };
      name.onselectstart = null;
      
      document.all[selectedEntity].onclick = null;
    }
    
    
    If the user presses the Enter key or fires the onBlur event, the renameEntityEnd method is called.

    This method receives the ID of the entity that is to be renamed. It makes the appropriate changes to both the XML Document Object Model (DOM) and the browser DOM.

    
    function renameEntityEnd(entityID) {
      var entity;
      var name;
    
      entity = xmlDoc.documentElement.selectSingleNode("//entity[@id='" + entityID +"']");
      name = document.all[entityID + "name"]
      name.style.cursor = "hand";
      name.contentEditable = false;
      name.onselectstart = function anonymous() { return false };
    
      entity.selectSingleNode("description").text = name.innerText;
    
      document.all[entityID].onclick = function anonymous() { clickOnEntity(document.all[entityID]) };
      document.body.onselectstart = returnFalse;
      saveXML();
    }
    
    

    Figure 2. Renaming an Entity

    Delete Capability

    The delete entity receives the ID of the entity that is to be deleted. The function first checks to make sure the entity does not contain children. If the entity contains children, an error will be displayed.

    After performing this check, this function then removes the entities node in both the XML DOM and the browser DOM.

    
    function deleteEntity() {
      var entity;
      
      entity = xmlDoc.documentElement.selectSingleNode("//entity[@id='" + selectedEntity +"']");
    
      if(entity.selectSingleNode("contents").childNodes.length > 0) {
        displayError("You cannot remove an entity that contains children. First remove all children."
    
    ., 8000);
      }
      else {
        entity = entity.parentNode.removeChild(entity)
        document.all[selectedEntity].removeNode(true)
    	saveXML();
      }
    }
    
    

    Redirecting the User

    The version of the tree available for download within this article includes a simple redirect method. This method receives a URL parameter of type string. You must call the redirect method from within your tree XML file. Figure 3 highlights the location of the onClick element used to call the redirect method.


    Figure 3. Location of onClick Element

    Figure 4 highlights the line within tree.xslt that inserts your onClick event during transformation.


    Figure 4 Inserting the onClick Event

    Interfacing with a Database

    If you are required to interface with a database, I can make the following recommendations. Use the XMLHTTP object within the init(), insertEntity(), updateEntity(), deleteEntity(), and renameEntityEnd() methods. These methods should contact your Web service and retrieve or send only the XML that is required to perform the requested operation.

    This approach will reduce load on your server tremendously. Instead of having to do a round trip back to the server, request the whole tree, and then render the whole tree again, you can make one easy behind-the-scenes call that updates your data, then updates only the area of the client browser that is required for your operation.

    Closing

    I hope the contents of this article will help increase the quality of your Web applications interface. If you have any questions, comments, or suggestions, please feel free to email me at the address listed in the author section.

    Demo and Download

    View a live demo of the folder tree (Works only in IE 5.5+ browsers).

    Download the newest version of the folder tree by clicking the example.zip hyperlink. This version includes both administration and viewing versions of the folder tree.

    Other Articles in This Series

    Advanced UI Design

    Part 1 -- Folder Tree Creation
    http://www.15seconds.com/Issue/010921.htm

    Part 2 -- Custom Context Menu Creation
    http://www.15seconds.com/issue/010927.htm

    Part 4 - Folder Tree Drag and Drop
    http://www.15seconds.com/issue/011129.htm

    Part 5 - Progress Indicator Creations
    http://www.15seconds.com/issue/011212.htm

    Part 6 -- Progress Indicator Usage
    http://www.15seconds.com/issue/020109.htm

    Part 7 -- Relational Folder Tree Lines
    http://www.15seconds.com/issue/020424.htm

    About the Author

    Joe Slovinski has been programming Web applications since 1993. For more information on the code in this article, contact the Joe Slovinski.

    Credits

    On a side note, I would like to thank Lee McGraw and Richard Spencer for helping me proof read and validate this document.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Supporting Products/Tools
    Stonebroom.ASP2XML
    Stonebroom.ASP2XML(c) is an interface component designed to make building applications that transport data in XML format much easier. It can be used to automatically pass updates back to the original data source.
    [Top]
    Other Articles
    Sep 22, 2005 - Implementing Remote Calling Without Using AJAX
    Right now the latest buzzword around town is AJAX. AJAX is an acronym for Asynchronous JavaScript and XML and is a method used to implement remote calling. The problem is that AJAX is only implemented in ASP.NET 2.0. This article will show you one way to implement remote calling without using AJAX or the XMLHttpRequest object. The technique outlined can even be used from classic ASP and is sufficient for most remote calling needs.
    [Read This Article]  [Top]
    Aug 18, 2005 - SQL Server 2005 XQuery and XML-DML - Part 3
    This article is the third and final installment of Alex Homer's series covering the new XML support in Microsoft SQL Server 2005. In it he covers updating the contents of xml columns, comparing traditional XML update techniques with XQuery, and using XQuery in a managed code stored procedure.
    [Read This Article]  [Top]
    Aug 11, 2005 - SQL Server 2005 XQuery and XML-DML - Part 2
    In the second part of his series on SQL Server 2005's new XML support, Alex Homer looks at extracting data from XML columns, comparing traditional XML data access approaches with XQuery, and combining XQuery and XSL-T.
    [Read This Article]  [Top]
    Aug 3, 2005 - SQL Server 2005 XQuery and XML-DML - Part 1
    Microsoft SQL Server 2005 now offers great support for and close integration with XML as a data persistence format. In the first article of his series examining this new support, Alex Homer offers an overview of how SQL Server 2005 stores XML documents and schemas, examines how it supports querying and manipulating XML documents, and provides a simple test application that allows you to experiment with XQuery.
    [Read This Article]  [Top]
    Jun 30, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 3, Cont'd
    In the final article of his series on reading and writing XML in .NET 2.0, Alex Homer looks at how the updated XML document store objects XmlDocument, XmlDataDocument and PathDocument can be used to read, persist and write XML documents and fragments more easily and more efficiently than in .NET 1.x.
    [Read This Article]  [Top]
    Jun 29, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 3
    In the final article of his series on reading and writing XML in .NET 2.0, Alex Homer looks at how the updated XML document store objects XmlDocument, XmlDataDocument and PathDocument can be used to read, persist and write XML documents and fragments more easily and more efficiently than in .NET 1.x.
    [Read This Article]  [Top]
    Jun 16, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 2, Cont'd
    Alex Homer continues his series on reading and writing XML in .NET 2.0. In part one, we focused on the reading side of things, examining the XmlReader and XmlReaderSettings classes. In this article, we move on to look at the XmlWriter and XmlWriterSettings classes, and how they can be used to write XML documents and fragments more easily and more efficiently than in version 1.x of .NET.
    [Read This Article]  [Top]
    Jun 15, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 2
    Alex Homer continues his series on reading and writing XML in .NET 2.0. In part one, we focused on the reading side of things, examining the XmlReader and XmlReaderSettings classes. In this article, we move on to look at the XmlWriter and XmlWriterSettings classes, and how they can be used to write XML documents and fragments more easily and more efficiently than in version 1.x of .NET.
    [Read This Article]  [Top]
    Jun 2, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 1, Cont'd
    In the first part of his series on reading and writing XML in .NET 2.0, Alex Homer discusses the XmlReader and XmlReaderSettings classes. The XmlReader exposes several useful new features and the all new XmlReaderSettings class makes it easy to generate single or multiple instances of an XmlReader with a range of useful properties.
    [Read This Article]  [Top]
    Jun 1, 2005 - Reading and Writing XML in .NET Version 2.0 - Part 1
    In the first part of his series on reading and writing XML in .NET 2.0, Alex Homer discusses the XmlReader and XmlReaderSettings classes. The XmlReader exposes several useful new features and the all new XmlReaderSettings class makes it easy to generate single or multiple instances of an XmlReader with a range of useful properties.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers