I would like to write some generic templates to transform collections of nodes into HTML lists. Each element of the collection should correspond to one list item. Ideally I would write
<xsl:apply-templates select="..." mode="ul"/>
along with a template which matches the individual elements in the selection, and the resulting HTML should look like
<ul>
<li>Transformation of first element in selection</li>
<li>Transformation of second element</li>
...
</ul>
That is, the content of each <li>
is generated by a non-generic template; but the list structure itself is generated by a generic one. The problem is to write a generic template that produces this list structure for any non-empty collection, and no output for an empty collection.
I tried the following:
<xsl:template match="*" mode="ul">
<xsl:if test="count(*) > 0">
<ul>
<xsl:apply-templates select="*" mode="li"/>
</ul>
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="li">
<li>
<xsl:apply-templates select="." />
</li>
</xsl:template>
But this doesn't work: each element of the collection will individually become a <ul>
. Conceptually, what I want is a way to transform the collection itself into a <ul>
, and then turn the elements of the collection into individual <li>
s.
Important here:
The test for the non-empty collection should be in the generic template, because I don't want to wrap every call to this template with a conditional, and I don't want to output empty <ul>
elements when the collection is empty.
In the XML documents I'm transforming, there is in general no common parent of the elements in the collection. That means I cannot transform the parent into the <ul>
and its children into <li>
s; there is no element in the source document which corresponds to the <ul>
.
Is this possible? The searching I've done increasingly suggests to me that it isn't, but that seems insane to me, since this must an incredibly common use case.
At least in theory, you should be able to do something like this:
<xsl:call-template name="ul">
<xsl:with-param name="nodes" select="..."/>
</xsl:call-template>
and then:
<xsl:template name="ul">
<xsl:param name="nodes"/>
<xsl:if test="$nodes">
<ul>
<xsl:apply-templates select="$nodes" mode="li"/>
</ul>
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="li">
<li>
<xsl:apply-templates select="." />
</li>
</xsl:template>
This would create a ul
wrapper around the selected nodes before applying the li
template to each element in the selection (which is, I think, what you call a collection).
Is this a worthwhile effort? Probably not. XML inputs come in a very wide variety of schemas, and rarely can a generic stylesheet fit all. Much easier to write a specific stylesheet that handles a known schema.