Extensible Stylesheet Language

According to its Wikipedia page, Extensible Stylesheet Language is a family of transformation languages which allows one to describe how files encoded in the XML standard are to be formatted or transformed.

netFORUM makes use of XSL in the XslGenerator user control which can be used on eWeb Page Detail pages developed in CMS.

XSL Tips for XslGenerator

XSL is a less forgiving language than plain HTML. If you do not enclose attributes with double-quotes or begin with an UPPERCASE <TD> and end with a lowercase </td> (instead of a case sensitive pairing of <td></td> or <TD></TD> then the XSL will not transform and the result will not load.

Additionally, many characters such as a nonbreaking space &nbsp; will throw an error in some cases; you must instead use numeric character references such as &#160; instead. (See http://www.w3.org/TR/REC-xml/ for more information.)

For ease of editing XSL, you might want to use Notepad or another editor such as Cooktop to manipulate the XSL, then copy-and-paste it into the CMS or save the XSL as a static file and use the {BeginXslFileName} option. XSL is a very powerful tool and Abila recommends that the web developer get a resource book or training to harness the language’s powerful features. Check out some of the web sites listed in the External Links section below for some tutorials.

Here are some common XSL formatting tips:

Include other XSL Files

You can include other XSL files inside an XSL file to centralize your code.

According to DevGuru:

The xsl:include element is used to add (include) a stylesheet to another. You can repeat this element in order to include more than one stylesheet.
The definitions and template rules of the included stylesheet are treated equal to the definitions and template rules of the including stylesheet. The effect is the same as if the included stylesheet was actually part of the stylesheet with which it is being included.
An analogy can be made about this element with regard to the #include used in the C computer language.

Example:

<xsl:stylesheet   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   version="1.0">
<xsl:include href="header_template.xsl" />

This can be a very powerful way of consolidating and centralizing your code and avoiding redundant, repetitive code. Imagine if you had a dozen content details using XslGenerator, and they all shared certain common include files. If you have to make a change, you can make the change in one place and it will ripple down to all of them.

Suppressing a blank value

<xsl:if   test="Fax[.!='']">
<b>Fax:</b>&#160;<xsl:value-of select="Fax" />
</xsl:if>

XML Elements with a Blank Space

If you have an XML element with a blank space then this will fail:

Company: &#160;<xsl:value-of select="Company Name" /></DIV>

But this will work:

Company: &#160;<xsl:value-of   select="Company_x0020_Name"   /></DIV>

The characters _x0020_ represent the blank space. You will have a white space if the webservice returns XML formatted such as:

<Company_x0020_Name>Acme</Company_x0020_Name>

XML Element names may not have a white space in them.

If you are using a SQL statement or Stored Procedure as your XML data source, then you will see this example if you were to write SQL such as this:

SELECT [Company Name] = org_name FROM co_organization (nolock)

To circumvent this entire problem, if you control the SQL data source, simply do not use blank spaces:

SELECT [Company_Name] = org_name FROM co_organization (nolock)

Outputting a clickable URL hyperlink

Here is the XSL to output a clickable URL hyperlink to a new page, assuming that the XML element for the URL is called Website and there is an XML element called company:

<a   href="{Website}"   target="_blank">
<xsl:value-of select="company"/>
</a>

The actual HTML source output will be:

<a href="http://www.abcindustriesonline.com" target="_blank">ABC Industries</a>

Here is another example. Note that we use &amp; for an ampersand (&) instead of just the & character. Using just the & character will cause an error.

<a   href="DynamicPage.aspx?WebCode=postpage&amp;Key={wps_key}">
<xsl:value-of select="wps_title" />
</a>

Outputting a clickable MAILTO hyperlink

Here is the XSL to output a MAILTO link that will open the user’s mail client, assuming that the XML element for the email address is called Email:

<A>
<xsl:attribute name="href">mailto:
<xsl:value-of select="Email"/>
</xsl:attribute>
<xsl:value-of select="Email"/>
</A>

Adding a “Back” hyperlink

<a   href="javaScript:history.back();">&#lt;&#lt; Back to Search Results</a>

Using the <br /> HTML Tag for a Carriage Return

Enter it like this:

<br   />&#160;

The character above is the “space” preceded by a <br /> tag.

Checking the Value of an Element

Use the <xsl:if test> tag to test the value of an element:

<xsl:if   test="a91_code='Script'">
<textarea cols="55" rows="8">
<xsl:value-of select="a91_notes" />
</textarea>
</xsl:if>
<xsl:if test="a91_code!='Script'">
<span class="bodyTXT">
<P>
<xsl:value-of select="a91_notes" />
</P>
</span>
</xsl:if>

In the example above, we are checking the value of the a91_code element.

Replace Carriage Returns with < BR >

Suppose you have an element called <ses_description/> and this element typically has a large amount of text with carriage return line feeds (aka /n). The XML might look like this:


When this is output throught XSL as HTML, instead of having line breaks, the text just keeps running into each other in one loooOOOOooong unreadable paragraph as shown below:


To solve this, use the following.

Assuming you have this:

<xsl:value-of   select="ses_description"   />

replace it with this:

<xsl:call-template   name="AddBR">
<xsl:with-param name="text" select="ses_description"/>
</xsl:call-template>

The above calls a template named AddBR which you will need to include. You might want to place this function in a separate utility file and then issue an include directive to pull this utility file into any of your XSL code that needs to reference it. Here is the template AddBR:

<!-- Replace \n with <br> -->
<xsl:template name="AddBR">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '&#xa;')">
<xsl:value-of select="substring-before($text, '&#xa;')"/>
<br/>
<xsl:call-template name="AddBR">
<xsl:with-param name="text" select="substring-after($text,'&#xa;')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

With this improvement, the page will now appear like this:


Thanks to Francis Huang for discovering this.

Output a Database Column Containing HTML

If you have a database column that contains HTML, and you want to output this in XSL, then you must use the "disable-output-escaping" property in this way:

<xsl:value-of   select="wps_html_description"   disable-output-escaping="yes"   />

If you don't do this, then you will get errors because the XSL will not be able to render the HTML tags contained in the actual data.

Displaying a Message Saying No Results

If you would like to have a message telling the user, "No results" or "Sorry, your search term did not have any matches" or something similar, then you can use this syntax:

<xsl:if   test="count(item) = 0">
No results!
</xsl:if>

where "item" is the name of the repeated node.

Example of this usage in a broader context:

<xsl:template   match="/root">
<h3>Search for an XYZ certified professional:</h3>
<table width="100%" border="1">
<tr>
<td>First</td>
<td>Middle</td>
<td>Last</td>
</tr>
<xsl:if test="count(item) = 0">
<tr>
<td colspan="3">No results</td>
</tr>
</xsl:if>
<xsl:apply-templates select="item" />
</table>
</xsl:if>
</xsl:template>
 
<xsl:template match="item">
<tr>
<td><xsl:value-of disable-output-escaping="yes" select="ind_first_name" /></td>
<td><xsl:value-of disable-output-escaping="yes" select="ind_mid_name" /></td>
<td><xsl:value-of disable-output-escaping="yes" select="ind_last_name" /></td>
</tr>
</xsl:template>

We have a header called "Name" that will always display. If there are no records in the "item" node, then the "No results" text will display. When the "apply-templates" command runs for the "item" node, if there is nothing in item, then nothing will display in this part of the page.

If you do not want column headers to display for no results, then add an additional "test" condition around the column headers:

<table   width="100%"   border="1">
<xsl:if test="count(item) != 0">
<tr>
<td>First</td>
<td>Middle</td>
<td>Last</td>
</tr>
</xsl:if>
<xsl:if test="count(item) = 0">
<tr>
<td colspan="3">No results</td>
</tr>
</xsl:if>
 
<xsl:apply-templates select="item" />
 
</table>

If you are using the XslGenerator Pager, you cannot use the count() function because only a subset of the records will be contained in the XML nodes (defined by {BeginPagerProperties}). Instead, use the pagerResultsRecordsCount parameter:

<xsl:if   test="$pagerResultsRecordsCount = 0">
No results!
</xsl:if>

See FOR XML PATH SQL for tips on writing a SQL command that will work in conjunction with this XSL.

Displaying the Node Position

XSL's position() gives you information about which node is currently being processed.

This can be used to alternate the color of rows in a table:

<!-- every other row is gray -->
<xsl:template match="item">
<tr>
<xsl:if test="position() mod 2 = 0">
<xsl:attribute name="bgcolor">#dddddd</xsl:attribute>
</xsl:if>
<td>
<xsl:value-of disable-output-escaping="yes" select="title" />
</td>
</tr>
</xsl:template>

Compare a node's position() to the last() function to determine if the last node is being processed:

<!-- outputs a list of titles like: "one, two, three" (with no comma on the end) -->
<xsl:template match="item">
<xsl:value-of disable-output-escaping="yes" select="title" />
<xsl:if test="position() != last()">,&#160;</xsl:if>
</xsl:template>

The position() function can also be used to display numbers in a table:

<!-- outputs:
Row Number 1
Row Number 2
Row Number 3 -->
<xsl:template match="item">
<tr>
<td>
Row Number <xsl:value-of disable-output-escaping="yes" select="position()" />
</td>
</tr>
</xsl:template>

If you are using the XslGenerator Pager, you cannot use the position() function by itself because only a subset of the records will be contained in the XML nodes (defined by {BeginPagerProperties}). Instead, use it in conjunction with the pagerResultsRecordsFrom parameter:

<!-- outputs:
Row Number 4
Row Number 5
Row Number 6 on page #2 -->
<xsl:template match="item">
<tr>
<td>
Row Number <xsl:value-of disable-output-escaping="yes" select="position() + $pagerResultsRecordsFrom - 1" />
</td>
</tr>
</xsl:template>

External Links

See Also