Latest: Express.js Route Middleware

Content with Style

Web Technique

Dynamic tables with XSLT

by Pascal Opitz on June 1 2005, 09:08

Required knowledge

To get into what I want to do in this tutorial, you should have a basic understanding of what XSLT is and how you can get it running.
I explained a couple of ways in my basic tutorial. Maybe it's worth reading it.

You can download the example-files, coming with my little development-javascript for MSXML.

Processing

Again you are forced to make your choice! How are you getting the variables passed to your XSL? I don't know! It depends on the processor you are using. I'll have an example-file for javascript and MSXML and PHP using sablotron provided in the zip-file. The links to the output will be transformed by using PHP and sablotron. I just pass querystrings to the processor, so you could use pretty much the same thing in ASP, PHP, JSP, whatever! Just to make the level of abstraction completely clear, let me AGAIN summarize what the file must do:

  1. Loading XML
  2. Loading XSL
  3. Passing querystring-variables to the processor
  4. Process the XML with the XSL

Now let's move on to the important bits!

Getting started

I'll use a pretty boring piece of data for this tutorial. Basically it should be data that is ready to be displayed as a table. Here is an excerpt:

<circus>
  <show>
    <start_day><![CDATA[31.12.2003]]></start_day>
    <end_day><![CDATA[26.01.2004]]></end_day>
    <postcode><![CDATA[81939]]></postcode>
    <city><![CDATA[München]]></city>
    <address><![CDATA[Kieferngarten]]></address>
    <artist>
      <key><![CDATA[KNO 01]]></key>
      <name><![CDATA[Esad Jones]]></name>
      <program><![CDATA[waghalsige Stunts]]></program>
    </artist>
  </show>
</circus>

Like you see, we're able to get a nice table out of that. We'll start by creating a basic HTML-table by parsing the whole document. The XSL for that look somehow like this:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="xml" indent="yes" encoding="utf-8"/> 

<xsl:param name="smode" />

<xsl:template match="circus">
  
  <html>
    <head>
      <title>mytable</title>
    </head>
    <body>
    
      <xsl:call-template name="show" />
    
    </body>
  </html>

</xsl:template> 
            
<xsl:template name="show">
  <h1>show overview</h1>

  <table>
    <thead>
      <tr>
        <th>start_day</th>
        <th>end_day</th>
        <th>postcode</th>
        <th>city</th>
        <th>address</th>
        <th>view artists</th>
      </tr>
    </thead>
    
    <tbody>
      <xsl:apply-templates />
    </tbody>
  </table>
</xsl:template>


<xsl:template match="show" name="srow">
  <tr>
    <td><xsl:value-of select="current()/start_day" /></td>
    <td><xsl:value-of select="current()/end_day" /></td>
    <td><xsl:value-of select="current()/postcode" /></td>
    <td><xsl:value-of select="current()/city" /></td>
    <td><xsl:value-of select="current()/address" /></td>
    <td></td>
  </tr>
</xsl:template>

</xsl:stylesheet>

You can watch the output here, the XSL is here.

Sorting

First thing we're going to do is to add dynamic sorting. This works using the XSL-Element <xsl:sort /> in combination with a variable. We'll insert a parameter into our XSL on top and reform the th-content in two of our collumns.

          <th><a href="?smode=postcode">postcode</a></th>
          <th><a href="?smode=city">city</a></th>

So now the variable “smode” will be passed to the processor. Buit we have to introduce it within the XSL as well. So within the first template we'll declare it with this line of code:

  <xsl:param name="smode" />

Now we can check the variable and choose the right <xsl:sort /> . I do this by wrapping it into an additional template, so I can reuse it later. Let's have a look:

<xsl:template name="sorting">
  <xsl:choose>
    <xsl:when test="contains($smode, 'postcode')">
    <xsl:apply-templates>
      <xsl:sort select="postcode" order="ascending" data-type="text" />
    </xsl:apply-templates>
    </xsl:when>
    <xsl:when test="contains($smode, 'city')">
    <xsl:apply-templates>
      <xsl:sort select="city" order="ascending" data-type="text" />
    </xsl:apply-templates> 
    </xsl:when>
    <xsl:otherwise>
    <xsl:apply-templates />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template> 

Now, instead of just using <xsl:apply-templates /> we'll call the template we created with a simple <xsl:call-template />. Have a look at the transformed output and make sure you understand the XSL.

Details

Alright, that's a nice beginning. But what is that link for, called “view artists”? Well, that's where we get to the interesting point. Obviously it's possible to display the artists for one show as a table on it's own. But how are we going to do that? We'll AGAIN use variables! What we need is a way to identify a row and then display the matching content. I'll use UID. Anyway, let's give it a go. We just need to use position(). Like that we can identify the row for which we want to see the sub-nodes. Because we use the position, we need to keep the sorting-mode as well, otherwise we select the wrong node when we sorted before. In the empty collumn of the table I insert this:

	<a href="?ident={position()}&smode={$smode}">artists</a>

Obviously I need somehow a way to check if the variable “ident” is set. This is why I change the <xsl:call-template name="show" /> to this:

      <xsl:choose>
        <xsl:when test="not($ident)">
        <xsl:call-template name="show" />
        </xsl:when>
        <xsl:otherwise>
        <xsl:call-template name="artist" />
        </xsl:otherwise>
      </xsl:choose>

So now I need an additional template called “artist”, which in this case just changes the table-structure and inserts a back-button.

<xsl:template name="artist">
  <h1>artists overview</h1>

  <a href="?smode={$smode}">back</a>

  <table>
    <thead>
      <tr>
        <th>name</th>
        <th>program</th>
      </tr>
    </thead>

    <tbody>
      <xsl:call-template name="sorting" />
    </tbody>
  </table>
</xsl:template>

As you might already have noticed, the sorting-wrapper will work like it did before as well. That's why we have to check the ident-variable within the applied template, which is the one named “srow”. I will create a new template called “artistrow”, so the table-structure is right.

<xsl:template match="show" name="srow">
  <xsl:choose>
    <xsl:when test="not($ident)">
    <tr>
      <td><xsl:value-of select="current()/start_day" /></td>
      <td><xsl:value-of select="current()/end_day" /></td>
      <td><xsl:value-of select="current()/postcode" /></td>
      <td><xsl:value-of select="current()/city" /></td>
      <td><xsl:value-of select="current()/address" /></td>
      <td><a href="?ident={position()}&smode={$smode}">artists</a></td>
    </tr>
    </xsl:when>
    <xsl:otherwise>
    <xsl:if test="position() = $ident">
      <xsl:for-each select="current()/artist">
        <xsl:call-template name="artistrow" />
      </xsl:for-each>
    </xsl:if>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>


<xsl:template name="artistrow">
  <tr>
    <td><xsl:value-of select="current()/name" /></td>
    <td><xsl:value-of select="current()/program" /></td>
  </tr>
</xsl:template>

The transformed output looks like this, the XSL like this .

Nearly done

Now we have both views, all we need is a bit of styling via CSS and we have a nice looking dynamic application where most of the logic happens within the XSL.

Comments

  • I wonder why it doesn’t work when I download both xml file & xsl file from this sample. It show output but once i tried to click on either one link for sorting, it doesn’t work as what sample shown.

    What is the problem? Do you miss out some line of the code??

    by angie on June 12 2006, 05:43 - #

  • @angie: I don’t think so, as it works fine in my example files when I run them on my machine.
    Maybe you can post your platform and PHP version, including which parser you’re using?

    Depending on which PHP version and platform you’re using it might be neccessary to include the “file://” protocol in the xml and xsl path in order to get it working:

    
    //filepath on windows
    $xml = 'file://' . str_replace("\", "/", getcwd()) . "/data.xml";
    $xsl = 'file://' . str_replace("\", "/", getcwd()) . "/example.xsl";
    
    //process the file and echo the result
    echo xslt_process($my_xslt,$xml,$xsl,NULL,array(),$arguments);
    

    by Pascal Opitz on June 12 2006, 06:04 - #

  • I am interested in how you get the variables that are passed into the XSL. It seems you are somehow taking them from the URL. You have examples of PHP and JavaScript, but I do not understand how this code works with the XSL. How does your XSL know that it needs to use PHP or JavaScript?

    Here is another question that I am interested in. How would this be done in JSP?

    This page is very helpful. Thank you for the great information thus far.

    by Mike on June 28 2006, 16:14 - #

  • Hi Mike. Usually the XSL is transfomed by a processor.
    You pass the XML, the XSL and some optional arguments to this processor. In this case I just pass the whole GET collection.

    Have a look in the function call for the transformation:
    
    $arguments = $_GET;
    xslt_process($my_xslt,$xml,$xsl,NULL,array(),$arguments);
    
    You can do this in javascript, c# and in PHP very easily. I have no idea how such a thing would look exactly in JSP, but I assume it will be pretty much the same.

    by Pascal Opitz on June 28 2006, 16:41 - #

  • Thanks for the pointer. I guess I still need a little more clearing up.

    Where would this code (that contains the function call for the transformation) go? If I use JavaScript in my XSL style sheet, would I need to create a function (with this code) inside the script tags and call it somewhere in the style sheet? Would it be better to keep the JavaScript in a separate file?

    I appreciate your time and help! Maybe I’ll finally be able to figure this out.

    by Mike on June 29 2006, 15:24 - #

  • Mike, please take some time and look at source of the example files. Then you shouldn’t have any problem to unscramble these questions yourself.

    by Pascal Opitz on June 29 2006, 17:47 - #

  • no sablatron in ZIP file

    by Mikkel on July 6 2006, 05:48 - #

  • Mikkel:
    You can find sablotron here.
    It’s a server extension, so you will have to install it first.

    by Matthias on July 6 2006, 09:08 - #

  • Hi
    First of all i would like to thank you for such a good tutorial. With the same code i am trying to display to two dynamic tables.
    In the main body i am calling 2 templates to display:
    xsl:call-template name=”onuVlanEntry”
    xsl:call-template name=”VoipLineEntry”

    In the tbody i am specifying this:
    xsl:apply-templates

    in the both the table there are 2 rows. Now in both the tables it is displaying 4 rows. In the tbody
    xsl:call-template name=”onuVlanEntryRows”
    is not working. Can you plaease help me out . I am not using sort. simple two table data display.

    Thanks In advance
    Nilesh

    by Nilesh on December 21 2006, 17:37 - #

  • When I run it, I got the following error message:

    Fatal error: Call to undefined function: xslt_create() in /home/cws/public_html/resources/xsl_tables/simple_table/transform.php on line 3

    Please help me .

    by casey on September 10 2008, 23:31 - #

  • casey: Make sure you have an XSL processor in your PHP installation. Now, the example I wrote here years ago works with Sablotron, but there is a new, built in processor in PHP5. Check the manual for how to instanciate that one.

    by Pascal Opitz on September 11 2008, 03:08 - #

  • When I click the "You can view the output here" link, or the link "You can view the transformed output here", it gives the error: Fatal error: Call to undefined function xslt_create() in /home/cws/public_html/resources/xsl_tables/simple_table/transform.php on line 3

    by Mark on October 19 2010, 09:54 - #

  • Mark, please read the comments above.

    by Pascal Opitz on October 20 2010, 16:36 - #


Comments for this article are closed.

Advertisement
Advertisement