XSLT - dynamic meshing

I create a dynamic table (grid) using xslt,

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="pRowId" select="generate-id()"/> </xsl:apply-templates> </tr> </xsl:template> <xsl:template match="Column/@*"> <xsl:param name="pRowId"/> <td width="50%"> <xsl:value-of select= "key('RowAttribsByName', concat($pRowId, '|', .) ) "/> </td> </xsl:template> </xsl:stylesheet> 

XML data:

 <TableData> <Columns> <Column Name="ID" Hidden="true" /> <Column Name="Name" Caption="Item Name" Link="Yes" Sort="Yes"/> <Column Name="Desc" Caption="Item Description" /> </Columns> <Rows> <Row ID="0" Name="A" /> <Row ID="1" Name="B" Desc="Some description"/> <Row ID="3" Name="C" /> </Rows> </TableData> 

Expected Result:

 <table border="1"> <tbody> <tr> <td> <a onclick="javascript:SortColumn('Item Name')">Item Name</a> </td> <td> <a onclick="javascript:SortColumn('Item Description')">Item Name</a></td> </tr> <tr> <td width="50%"> <a onclick="javascript:OpenDifferentPage('A','0')">A</a> </td> <td width="50%"></td> </tr> <tr> <td width="50%">B</td> <td width="50%">Some description</td> </tr> <tr> <td width="50%">C</td> <td width="50%"></td> </tr> </tbody> </table> 

I start at XSLT,

I want to check here that if the column has the attribute "Link" = "yes", then I need to display the data between the anchor tag (Name).

I create many complex functions in this column. Thus, here I can create a template for certain columns ( The column is 15, but depends on the choice of the user, if the user selects 8 columns to display, and must also support ORDER of Columns )

It’s best that I can create new templates for all columns with column order according to the column data.

Thank you for your expectation.

+2
source share
2 answers

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.

+2
source

This XSLT 1.0 style sheet ...

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" /> <xsl:strip-space elements="*" /> <xsl:key name="linked-cols" match="Column[@Link='Yes']" use="@Name" /> <xsl:template match="TableData"> <table border="1"> <tbody> <xsl:variable name="cols" select="count(Columns/Column[not(@Hidden='true')])" /> <xsl:apply-templates> <xsl:with-param name="col-width" select="concat(100 div $cols,'%')" /> </xsl:apply-templates> </tbody> </table> </xsl:template> <xsl:template match="Columns"> <tr> <xsl:apply-templates /> </tr> </xsl:template> <xsl:template match="Column[@Hidden='true']" /> <xsl:template match="Column[@Sort='Yes']"> <td> <a onclick="javascript:SortColumn('{@Caption}')"><xsl:value-of select="@Caption" /></a> </td> </xsl:template> <xsl:template match="Column"> <td><xsl:value-of select="@Caption" /></td> </xsl:template> <xsl:template match="Rows"> <xsl:param name="col-width" /> <xsl:apply-templates> <xsl:with-param name="col-width" select="$col-width" /> </xsl:apply-templates> </xsl:template> <xsl:template match="Row"> <xsl:param name="col-width" /> <tr> <xsl:variable name="Row" select="." /> <xsl:for-each select="../../Columns/Column[not(@Hidden='true')]"> <td width="{$col-width}"> <xsl:variable name="col-name" select="@Name" /> <xsl:apply-templates select="$Row/@*[local-name() = $col-name]" /> </td> </xsl:for-each> </tr> </xsl:template> <xsl:template match="Row/@*"> <xsl:value-of select="." /> </xsl:template> <xsl:template match="Row/@*[key('linked-cols',local-name())]"> <a onclick="javascript:OpenDifferentPage('{.}','{../@ID}')"><xsl:value-of select="." /></a> </xsl:template> </xsl:stylesheet> 

... when applied to this document ...

 <TableData> <Columns> <Column Name="ID" Hidden="true" /> <Column Name="Name" Caption="Item Name" Link="Yes" Sort="Yes"/> <Column Name="Desc" Caption="Item Description" /> </Columns> <Rows> <Row ID="0" Name="A" /> <Row ID="1" Name="B" Desc="Some description"/> <Row ID="3" Name="C" /> </Rows> </TableData> 

... will give ...

 <!DOCTYPE html SYSTEM "about:legacy-compat"> <table border="1"> <tbody> <tr> <td><a onclick="javascript:SortColumn('Item Name')">Item Name</a></td> <td>Item Description</td> </tr> <tr> <td width="50%"><a onclick="javascript:OpenDifferentPage('A','0')">A</a></td> <td width="50%"></td> </tr> <tr> <td width="50%"><a onclick="javascript:OpenDifferentPage('B','1')">B</a></td> <td width="50%">Some description</td> </tr> <tr> <td width="50%"><a onclick="javascript:OpenDifferentPage('C','3')">C</a></td> <td width="50%"></td> </tr> </tbody> </table> 
+1
source

Source: https://habr.com/ru/post/1386228/


All Articles