Dynamic tables with XSLT
published 20 February 2005
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:
- Loading XML
- Loading XSL
- Passing querystring-variables to the processor
- 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.
Comment
- 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??
— angie 12 June, 11:43am # - @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);
— Pascal 12 June, 12:04pm # - 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.
— Mike 28 June, 10:14pm # - 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:
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.$arguments = $_GET; xslt_process($my_xslt,$xml,$xsl,NULL,array(),$arguments);
— Pascal Opitz 28 June, 10:41pm # - 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.
— Mike 29 June, 9:24pm # - 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.
— Pascal Opitz 29 June, 11:47pm # - no sablatron in ZIP file
— Mikkel 6 July, 11:48am # - Mikkel:
You can find sablotron here.
It’s a server extension, so you will have to install it first.
— Matthias Willerich 6 July, 3:08pm # - 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
— Nilesh 21 December, 11:37pm # - 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 .
— casey 11 September, 5:31am # - 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.
— Pascal Opitz 11 September, 9:08am #
Quick links
Other people's articles that we think you might be interested in:
- Ajaxian » Web Inspector: Looking good, and profiling nicely
- smush it!
- cssdoc - Trac
- CSS Systems for writing maintainable CSS | Natalie Downe
- The PNG Gamma Dilemma - Trevor Morris Photographics
Related Amazon links
Want to buy a cheap laptop for your design work? read laptop reviews at laptopical.com