XSLT para transformar lista em tabela com colunas determinado dinamicamente
-
06-07-2019 - |
Pergunta
Eu preciso desse XML,
<list columns="3">
<item>martin</item>
<item>donald</item>
<item>whistler</item>
<item>mother</item>
<item>carl</item>
<item>liz</item>
<item>cosmo</item>
</list>
para ficar assim:
<table>
<tr>
<td>martin</td>
<td>donald</td>
<td>whistler</td>
</tr>
<tr>
<td>mother</td>
<td>carl</td>
<td>liz</td>
</tr>
<tr>
<td>cosmo</td>
<td></td>
<td></td>
</tr>
</table>
Quando columns="4"
, ele deve ser parecido com isto:
<table>
<tr>
<td>martin</td>
<td>donald</td>
<td>whistler</td>
<td>mother</td>
</tr>
<tr>
<td>carl</td>
<td>liz</td>
<td>cosmo</td>
<td></td>
</tr>
</table>
Todas as dicas sobre o que o arquivo XSLT deve ser semelhante? Perto quanto eu posso dizer, isso requer algum tipo de loop (recursão?), Mas não tenho certeza se há uma maneira mais elegante.
Solução
A abordagem que eu tomaria é combinar itens na 1ª, 4ª, posições 7ª usando a função 'mod' da posição () em cada item.
Depois de combinar cada uma dessas item, basta percorrer os seguintes irmãos com base na contagem de coluna.
Para a última linha, onde pode haver itens suficientes para completar a linha, não há um modelo recursivo para adicionar nas células vazias com base em quantos itens foram na última linha.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Global variable to get column count -->
<xsl:variable name="columns" select="number(/list/@columns)"/>
<!-- Match the root node -->
<xsl:template match="list">
<table>
<!-- Match items in the 1st, 4th, 7th positions, etc (or whatever the column variable holds) -->
<xsl:apply-templates select="item[position() mod $columns = 1]"/>
</table>
</xsl:template>
<xsl:template match="item">
<tr>
<!-- Output the current item -->
<td>
<xsl:value-of select="."/>
</td>
<!-- Output the following items based on the number of required columns -->
<xsl:for-each select="following-sibling::item[position() < $columns]">
<td>
<xsl:value-of select="."/>
</td>
</xsl:for-each>
<!-- Add in any empty cells if numberof following items is not sufficient -->
<xsl:call-template name="emptycell">
<xsl:with-param name="cellcounter" select="count(following-sibling::item[position() < $columns]) + 1" />
</xsl:call-template>
</tr>
</xsl:template>
<!-- Recursive template to add in empty cells when there are not enough items to complete a row -->
<xsl:template name="emptycell">
<xsl:param name="cellcounter" />
<xsl:if test="$cellcounter < $columns">
<td></td>
<xsl:call-template name="emptycell">
<xsl:with-param name="cellcounter" select="$cellcounter + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Outras dicas
algo que funciona, mas parece um pouco feio:. Posição abuso ()
<xsl:param name="columns">4</xsl:param>
<xsl:template match="list">
<xsl:variable name="theList" select="."/>
<xsl:for-each select="//*[position()<(count(item) / $columns)]>
<xsl:variable name="idx" select="position()"/>
<tr>
<xsl:for-each select="//*[position()<$columns]">
<td><xsl:value-of select="$theList/item[position() + $idx * $columns]"/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>
Há outras maneiras: por exemplo, selecionar primeiro todos os nós que divide com descanso 0 por colunas na lista, e depois caminhar sobre aqueles
.http://www.ibm.com/developerworks/library/ x-tipnodst.html por um longo descrição da técnica acima.
Eu sugiro usar colunas CSS3 . A especificação se tornará uma Recomendação Candidate (convite à apresentação de implementações), em vez breve, e já está implementado no Gecko e WebKit (Firefox, Safari, Chrome), com prefixos de fornecedores.
Código:
ul { -moz-column-count: 3; -webkit-column-count: 3; }