For those new to XSLT, you're already starting out really well here, especially with xsl: key .
To answer your immediate question, what could you do, instead of having a single template matching the attributes of the Column elements, as of now ....
<xsl:template match="Column/@*">
You may have an explicit template matching it if Link is installed.
<xsl:template match="Column[@Link='Yes']/@*">
In this template, you can add additional code to display the link a . Note that this might be a little easier if you pass the actual Row as a parameter, as opposed to the generate-id value for the string, since it will be a little less verbose to get the ID attribute.
<xsl:template match="Column[@Link='Yes']/@*"> <xsl:param name="pRow"/> <xsl:variable name="pRowId" select="generate-id($pRow)"/> <xsl:variable name="pValue" select="key('RowAttribsByName', concat($pRowId, '|', .))" /> <td width="50%"> <a onclick="javascript:OpenDifferentPage('{$pValue}','{$pRow/@ID}')"> <xsl:value-of select="$pValue"/> </a> </td> </xsl:template>
Note the use of attribute value templates here when creating onclick attributes. Curly braces indicate an expression that should be evaluated, not printed literally.
For this template to work, you also need to change the other template to explicit matching attributes, where the Link attribute is not set to Yes
<xsl:template match="Column[not(@Link='Yes')]/@*">
This is due to the fact that otherwise the original template will correspond to the Link as Yes case with the same priority as the new template that is not allowed.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:key name="RowAttribsByName" match="Row/@*" use="concat(generate-id(..), '|', name())"/> <xsl:variable name="vColNames" select="/*/Columns/*[not(@Hidden = 'true')]/@Name"/> <xsl:template match="/*"> <table border="1"> <tr> <xsl:apply-templates select="Columns/*"/> </tr> <xsl:apply-templates select="Rows/Row"/> </table> </xsl:template> <xsl:template match="Column[not(@Hidden = 'true')]"> <td> <xsl:value-of select="@Caption"/> </td> </xsl:template> <xsl:template match="Row"> <tr> <xsl:apply-templates select="$vColNames"> <xsl:with-param name="pRow" select="."/> </xsl:apply-templates> </tr> </xsl:template> <xsl:template match="Column[not(@Link='Yes')]/@*"> <xsl:param name="pRow"/> <xsl:variable name="pRowId" select="generate-id($pRow)"/> <xsl:variable name="pValue" select="key('RowAttribsByName', concat($pRowId, '|', .))"/> <td width="50%"> <xsl:value-of select="$pValue"/> </td> </xsl:template> <xsl:template match="Column[@Link='Yes']/@*"> <xsl:param name="pRow"/> <xsl:variable name="pRowId" select="generate-id($pRow)"/> <xsl:variable name="pValue" select="key('RowAttribsByName', concat($pRowId, '|', .))"/> <td width="50%"> <a onclick="javascript:OpenDifferentPage('{$pValue}','{$pRow/@ID}')"> <xsl:value-of select="$pValue"/> </a> </td> </xsl:template> </xsl:stylesheet>
This approach has several disadvantages. There are several duplicate codes, and it would be harder to manage if you had other attributes of the Column elements that influenced the output of the columns.
Here is another version of XSLT that uses the mode element to re-map the Column attribute, allowing you to potentially display more elements if necessary
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:key name="RowAttribsByName" match="Row/@*" use="concat(generate-id(..), '|', name())"/> <xsl:variable name="vColNames" select="/*/Columns/*[not(@Hidden = 'true')]/@Name"/> <xsl:template match="/*"> <table border="1"> <tr> <xsl:apply-templates select="Columns/*"/> </tr> <xsl:apply-templates select="Rows/Row"/> </table> </xsl:template> <xsl:template match="Column[not(@Hidden = 'true')]"> <td> <xsl:value-of select="@Caption"/> </td> </xsl:template> <xsl:template match="Row"> <tr> <xsl:apply-templates select="$vColNames"> <xsl:with-param name="pRow" select="."/> </xsl:apply-templates> </tr> </xsl:template> <xsl:template match="Column/@Name"> <xsl:param name="pRow"/> <xsl:variable name="pRowId" select="generate-id($pRow)"/> <xsl:variable name="pValue" select="key('RowAttribsByName', concat($pRowId, '|', .))"/> <td width="50%"> <xsl:apply-templates select=".." mode="link"> <xsl:with-param name="pRow" select="$pRow"/> <xsl:with-param name="pValue" select="$pValue"/> </xsl:apply-templates> </td> </xsl:template> <xsl:template match="Column[@Link='Yes']" mode="link"> <xsl:param name="pRow"/> <xsl:param name="pValue"/> <a onclick="javascript:OpenDifferentPage('{$pValue}','{$pRow/@ID}')"> <xsl:apply-templates select="self::*" mode="value"> <xsl:with-param name="pRow" select="$pRow"/> <xsl:with-param name="pValue" select="$pValue"/> </xsl:apply-templates> </a> </xsl:template> <xsl:template match="Column" mode="link"> <xsl:param name="pRow"/> <xsl:param name="pValue"/> <xsl:apply-templates select="self::*" mode="value"> <xsl:with-param name="pRow" select="$pRow"/> <xsl:with-param name="pValue" select="$pValue"/> </xsl:apply-templates> </xsl:template> <xsl:template match="Column" mode="value"> <xsl:param name="pRow"/> <xsl:param name="pValue"/> <xsl:value-of select="$pValue"/> </xsl:template> </xsl:stylesheet>
Please note that in this example there are two templates for the link.
<xsl:template match="Column[@Link='Yes']" mode="link"> <xsl:template match="Column" mode="link">
In this case, the second one does not need to check the Link attribute. If the pattern simply matches the explicit name of the element, it will have a lower priority than the one qualified using the xpath expression. Therefore, the second template will never correspond to the case when the Link is Yes.