When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips

Sections:
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
XML Info
Information:
Feedback
Author an Article
ASP ASP.NET ASP FAQs Message Board Feedback
Print this page.
Published: Tuesday, October 10, 2000

Displaying the Data With an XSLT Style Sheet, Part 2

By Richard Chisholm


  • Read Part 1

  • In Part 1 we discussed the objective of this article and looked at the task from a high-level prospective. In this part we'll delve into the specifics!

    - continued -

    The XSLT Style Sheet
    XSLT appears very complicated at first look, but after a while you will see it as a fairly simple, even elegant language. XSL functions by using templates to display the data from a given node. However, unlike HTML, XSL is very unforgiving. Everything must be done exactly right, or your output will be way off. However, it's pickiness and many of its methods are similar to other languages ASP programmers are familiar with, namely SQL and VBScript. Just as an incorrect reference to a field in a table or an incorrect path using the FileSystemObject will get you nowhere, a wrong XPath in XSL will not work either. The major difference is that XSL won't inform you of the error, but SQL and VBScript will. But XPath is more than just a URI (Universal Resource Indicator) that acts similar to a hyperlink. XPath can be used to call functions, search the XML node for specific elements or nodes, and reference parameters and variables to filter the data.

    Now that I've covered some general concepts, click here to view the demo file (IE5 only, with an MSXML3 parser installed), and let's get into the details of the document. The first template in every XSL document is the root. This is not to be confused with the root element of the XML document, as the match="/" signifies. Before the root template however, is where the initializing information lays.

    <?xml version="1.0" encoding="UTF-8" ?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     <xsl:param name="OrderNum">0</xsl:param>
     <xsl:param name="Product">0</xsl:param>
    

    As you can see the first line is a standard XML declaration, since an XSL document is also an XML document (an important thing to remember, as you will see later). The second line is the namespace, which identifies this as an XSLT style sheet. The third and fourth elements are the parameters we will use to control the display of our XML. When using an xsl:param element, you can reference the value at any time in the document by putting a dollar sign ($) in front of the name. Thus, for this document it is $OrderNum for the order number, and $Product for the product number. Although these parameters apply to the whole document, you can create them for specific templates, much like declaring local variables in a function. These parameters have default value of zero, which will be explained in a little bit.

    The default template contains the HTML header, the JavaScript to access and change the parameters, and some initial html in the <body> section. This is the way I have seen most XSL style sheets done, and it seems to be the best way. Once you have built the general layout of the page, then call the first template to begin displaying the data. Separating as much of the layout of the page from where you display the data makes the XSL document easier to understand and edit.

    The first template called is for the root of the XML document, in this case Order_Summary. It does two things: display the name of the document, stored as an attribute of the root element, and create a drop down box. Displaying the name requires accessing the Name attribute in the XPath statement, in this the XSL tag <xsl:value-of select="@Name"/>. The @ symbol is used to access attributes. One thing to remember is that XPath is case sensitive, so in this case if @name had been used in the style sheet, nothing will be displayed. Creating the select box is a little more tricky and looks like this:

    <select size="1" name="Order" onChange="showOrder()">
      <option>Select An Order</option>
      <xsl:for-each select="Order_Details">
        <option>
          <xsl:attribute name="value">
          <xsl:value-of select="@Value"/>
          </xsl:attribute>
          <xsl:value-of select="@Value"/>
        </option>
      </xsl:for-each>
    </select>
    

    The xsl:for-each method is similar to a For loop. Here it selects each Order_Details node and creates an option tag with the OrderID as the value. When an order is selected the showOrder() function will take the selected value, and display the data in that order. The last line of code in the Order_Summary template is another xsl:apply-templates tag for the Order_Details. This snippet of code is also useful to show the way XSL renders HTML. As you can see, the <option> tag is separated from it's value and text, but because they are encapsulated by the <option> and </option> tags, the end result the desired HTML. The xsl:attribute tag inserts itself into the parent HTML tag, and the xsl:value-of tag places the text that is displayed in the drop box.

    Because the Order_Details template is much longer, instead of going through it line by line I will just discuss the different XSL tags. The output is a simple table that displays the order data. The first XSL tag inside the Order_Details template is <xsl:if test="@Value[.=$OrderNum]">. The If tag tests each Order_Details node to see if the OrderID value matches the set parameter OrderNum, similar to an If...Then conditional statement in VBScript. Because the OrderNum parameter is by default set to zero, nothing will be displayed until an order is selected. The function showOrder() is used to change the parameter.

    The next set of XSLT tags combine the functionality of a FOR...NEXT loop and the SELECT CASE statement. Their purpose is to loop through each Order Detail Element, and if there are no child elements (which would signify a new node containing the Product info), display the information in the Order_Detail element. If a child node is found, a second loop will find each product, and produce a button that will display the product information when clicked.

    <xsl:for-each select="Order_Detail">
      <xsl:choose>
        <xsl:when test="not(./*)">
          <tr>
            <td width="100"><xsl:value-of select="@Name"/></td>
            <td width="200"><xsl:value-of select="."/></td>
          </tr>
        </xsl:when>
    
        <xsl:otherwise>
          <td colspan="2">
            <p>Click on a Product to view it' info.</p>
            <xsl:for-each select="Product_Details">
              <input style="width:70px" id="Product" type="Button" onClick="showProduct()">
                <xsl:attribute name="value"><xsl:value-of select="@Value"/></xsl:attribute>
              </input>
            </xsl:for-each>
           </td>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
    

    With the xsl:for-each tag, the "select" attribute defines which XPath node will be used. Here I am selecting the child node, so just the name of that node will suffice. From this position (remember that XPath is relative) I could select the Product info by using the XPath "Order_Detail/Product_Details/Product_Detail". The xsl:for-each command loops through every node that fits the XPath statement. On the other hand, the xsl:choose, xsl:when, and xsl:otherwise work in tandem to create the functionality of a SELECT CASE statement. The xsl:when and xsl:otherwise lay inside the xsl:choose tags, with as many xsl:when tags as needed and one xsl:otherwise. Since there are one of each in the above code, the XSL tags are acting more like an IF...THEN...ELSE statement. The xsl:when uses a "test" attribute for the same purpose as the xsl:if tag. As you can see, to filter out unwanted XML elements a not function is used in the XPath (identical to using NOT in an IF statement in VBScript). Inside the parentheses, the period stands for the current context and the forward-slash and star mean any child elements, thus allowing only single XML elements. Also, a period can be used in the xsl:value-of tag to denote the current context (see the last template for an example of this).

    When the buttons that are displayed in the xsl:otherwise tag above are clicked on, new tables are displayed to the right of the original using a <div> tag and positioning styles that show the product and supply information. The rest of the templates, corresponding to Product and Supplier information, follow the same pattern, using xsl:for-each tags to produce simple tables that display the data. When creating tables you must account for the recursive nature of XSL. Because a template is applied to every element seperately, placing the <table> tags within the template will cause x number of tables, where x is the number of elements. Instead, wrap the <table> and </table> tags in the parent template around a xsl:apply-templates tag, and only place the <tr> and <td> tags inside. The last two templates in the example style sheet show this.

    Now that I've covered the XSLT tags, it is time to discuss how the different orders and products can be viewed by clicking a button or changing the select box. When reading about XSLT parameters, almost every source says that they can be set using VB, JavaScript, etc. Unfortunately, they usually go on to say that parameters cannot be changed once the document is rendered by XSLT. While this is literally true, no article or documentation I have seen does mentions that you can change a parameter and then re-apply the style sheet, which enables you to effectively make dynamic changes to your XSLT document.

    This is one of the most interesting, and confusing, aspects of XSLT. Because an XSL document is itself an XML document, it is possible to access the XSL document using the DOM (the whole style sheet can be accessed by the browser using the declaration style = document.XSLDocument;). Changing the value of a single parameter requires accessing is directly with the selectSingleNode() method. The function consists of four lines of code:

     function showOrder()
     {
        el = window.event.srcElement; //get the event element 
        objParam = document.XSLDocument.selectSingleNode("//xsl:param[@name='OrderNum']");
        objParam.childNodes.item(0).nodeValue = el.value;
        data.innerHTML = source.documentElement.transformNode(style);
     }
    

    The second JavaScript function, which shows the product and supplier data, is essentially the same script. The event element, either the select box value or the value embedded in the <input> tag, is grabbed, and then the parameter. Note that the parameter is found by matching the name attribute. Also, the two forward-slashes inside the selectSingleNode method tells the XSLT processor to start looking for the indicated node at the root of the document. In this instance it is not required, but would be if our starting point is somewhere else the document. On the third line, the event value is assigned to the parameter. While the code appears complicated, it follows a clear line of logic. Because the parameter value is not an attribute, we cannot access it directly with the setAttribute. Instead we access the nodeValue of what technically is the first child element, but is in fact the text of the parameter. Although Microsoft's MSXML3 implementation has a text property for DOM elements, it is read-only and cannot be used here. Finally, the newly altered style sheet is re-applied (using transformNode) to the area inside the data <div> tags where all our information is displayed.

    As with the conversion COM component, this XSLT style sheet shows that creating pages to display information from a database can be done quickly, and with little scripting on the client side. With a little bit of thought building an entire website based on XML and XSLT architecture is possible with few hassles. In addition, the XML/XSLT can be used inside an IFrame that only holds data, while the parent document contains the layout for the entire site. A simple JavaScript function can be used to match the height of the IFrame with the height of the XML/XSLT page every time the style sheet is re-applied. Creating an application like this can also lessen the load on the server, as all rendering of the pages is done by the browser instead of the server, which happens with most ASP pages. It is even possible to design applications for a disconnected environment that can be re-synchronized at the user's leisure.

    Hopefully this article has given you an idea of how XSLT can make an ASP programmer's life simpler and more enjoyable, both in the design. Although the code may be fairly simple, it allows XSLT developers to easily create complex documents by streamlining the process and making it more practical to transfer large data sets to the client while displaying only what is needed. Likewise, XSLT can be used instead of RDS, and because the XML document is highly structured, and features that sort or change the organization of the data can be written easily and require no additional connections to the server.

    Happy Programming!

  • By Richard Chisholm


    Attachments:

  • View the XML file
  • Download the support files (in ZIP format)


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article