2009-10-08 10 views
3

J'ai une liste des éléments que je veux diviser en différentes listes de 3. Le résultat final serait quelque chose comme ceci:Comment diviser un <xsl:foreach> en plusieurs parties?

<ul> 
    <li>element</li> 
    <li>element</li> 
</ul> 
<ul> 
    <li>element</li> 
    <li>element</li> 
</ul> 
<ul> 
    <li>element</li> 
    <li>element</li> 
</ul> 

Mon XSLT est comme ça, mais ça ne fonctionne pas, parce que je ne peut pas insérer </ul> et je ne peux pas insérer un signe inférieur (<).

<ul> 
    <xsl:for-each select="$myroot/item"> 
     <li></li> 

     <xsl:if test="position() mod $maxItemsPerColumn = 0"> 
      <!-- I want to close my ul, and start a new one here, but it doesn't work! --> 
     </xsl:if> 
    </xsl:for-each> 
</ul> 

Des idées? Merci d'avance!

+0

Il pourrait ne pas être vous laisser fermer l'UL à l'intérieur de la boucle depuis l'UL a commencé en dehors de la boucle. Essayez de tout déplacer à l'intérieur de la pour-chaque –

Répondre

9

Vous n'avez pas besoin de faire quelque chose de fantaisie comme la récursivité. Et bon seigneur, n'envisagez même pas d'utiliser CDATA.

Vous devez juste penser comme XSLT et demander: "Quel élément d'entrée dois-je transformer en élément de sortie?"

En supposant que chaque ul est censé contenir N item s, vous voulez transformer toutes les entrées Nième item, en commençant par la première, en ul:

<xsl:variable name="n" select="number(4)"/> 

<xsl:template match="/"> 
    <output> 
    <xsl:apply-templates select="/root/item[position() mod $n = 1]"/> 
    </output> 
</xsl:template> 

Chacun de ces item éléments devient un ul qui contient l'élément et chacun de ses frères et soeurs N-1 suivants:

<xsl:template match="item"> 
    <ul> 
    <xsl:for-each select=". | following-sibling::item[position() &lt; $n]"> 
     <li> 
     <xsl:value-of select="."/> 
     </li> 
    </xsl:for-each> 
    </ul> 
</xsl:template> 

En supposant un document d'entrée l ike ceci:

<root> 
    <item>1</item> 
    <item>2</item> 
    <item>3</item> 
    <item>4</item> 
    <item>5</item> 
    <item>6</item> 
    <item>7</item> 
    <item>8</item> 
    <item>9</item> 
</root> 

... vous obtenez cette sortie, si $n est réglé sur 4:

<output> 
    <ul> 
    <li>1</li> 
    <li>2</li> 
    <li>3</li> 
    <li>4</li> 
    </ul> 
    <ul> 
    <li>5</li> 
    <li>6</li> 
    <li>7</li> 
    <li>8</li> 
    </ul> 
    <ul> 
    <li>9</li> 
    </ul> 
</output> 
+0

@Robert Rossney: Sympa, propre et simple. +1 – Tomalak

+0

Génial! Fonctionne parfaitement! –

+0

Propre et ... simple ??? –

-1

vous pouvez faire

<xsl:text disable-output-escaping="yes"><![CDATA[</ul>]]></xsl:text> 

<xsl:text disable-output-escaping="yes"><![CDATA[<ul>]]></xsl:text> 

donc dans votre cas:

<ul> 
    <xsl:for-each select="$myroot/item"> 
     <li></li> 

     <xsl:if test="position() mod $maxItemsPerColumn = 0"> 
      <xsl:text disable-output-escaping="yes"><![CDATA[</ul>]]></xsl:text> 
      <xsl:text disable-output-escaping="yes"><![CDATA[<ul>]]></xsl:text>     
     </xsl:if> 
    </xsl:for-each> 
</ul> 

Mise à jour: j'avais supprimé après avoir lu les réponses de Pierre et Greg, mais j'ai décidé de le garder comme cela répond à votre question et cela pourrait s'avérer utile à quelqu'un, quelque part.

Mise à jour 2: Oui, je comprends pourquoi cela peut être horrible et inspirer Nazgûl comme la peur dans mes pairs et oui j'ai essayé de down-vote moi mais je pense que ce cette réponse peut être utile à quelqu'un dans l'avenir .

+4

C'est vraiment mal. –

+3

C'est ... horrifiant. Sérieusement, en utilisant * soit * CDATA ou "disable-output-escaping" dans XSLT est généralement un avertissement que vous faites une erreur conceptuelle quelconque. En utilisant * les deux *? –

+2

Je pense que vous vouliez dire "cette réponse peut être" préjudiciable ** à quelqu'un dans le futur ";-) – NickFitz

0

vous pouvez essayer quelque chose comme ça (non testé, mais vous avez l'idée)

(...) 
<xsl:call-template name="recursive"> 
<xsl:with-param name="root" select="$myroot"/> 
<xsl:with-param name="index" select="number(1)"/> 
</xsl:call-template> 
(...) 


<xsl:template name="recursive"> 
<xsl:param name="root"/> 
<xsl:param name="index"/> 
<ul> 
<li><xsl:value-of select="$root/item[$index]"/></li> 
<li><xsl:value-of select="$root/item[$index +1 ]"/></li> 
</ul> 
<xsl:if test="$root/item[$index+2]"> 
<xsl:call-template name="recursive"> 
<xsl:with-param name="root" select="$root"/> 
<xsl:with-param name="index" select="$index+2"/> 
</xsl:call-template> 
</xsl:if> 
</xsl:template> 
1

Vous pouvez le faire en utilisant une solution récursive:

<xsl:call-template name="group"> 
    <xsl:with-param name="items" select="$myroot/item" /> 
</xsl:call-template> 

<xsl:template name="group"> 
    <xsl:param name="items" /> 
    <xsl:if test="count($items) > 0"> 
    <ul> 
     <xsl:for-each select="$items[position() &lt;= 3]"> 
     <li>...</li> 
     </xsl:for-each> 
    </ul> 
    <xsl:call-template name="group"> 
     <xsl:with-param name="items" select="$items[position() > 3]" /> 
    </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

Ce que cela ne fait qu'appeler le modèle group pour la liste complète des articles. Le modèle group écrit les trois premiers éléments de la liste (ou moins s'il n'y en a pas trois) à l'intérieur des balises <ul> ... </ul>. Puis il s'appelle à nouveau avec le reste de la liste des éléments en omettant les trois premiers. Lorsque la liste est vide, le modèle group ne fait rien. XSLT est un langage très fonctionnel, et suivre les règles (c'est-à-dire ne pas utiliser disable-output-escaping pour cela) vous épargnera beaucoup de douleur et de souffrance dans le futur lorsque vous aurez besoin de modifier à nouveau vos templates.

Questions connexes