Как я могу разделить <xsl: foreach> на несколько частей? - PullRequest
3 голосов
/ 09 октября 2009

У меня есть список элементов, которые я хочу разделить на отдельные списки по 3. Конечный результат будет примерно таким:

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

Мой XSLT такой, но он не работает, потому что я не могу вставить </ul> и не могу вставить знак меньше (<).

<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>

Есть идеи? Заранее спасибо!

Ответы [ 4 ]

9 голосов
/ 09 октября 2009

Вам не нужно делать что-то необычное, например, рекурсию. И, Господи, даже не задумывайтесь над использованием CDATA.

Вы просто должны думать как XSLT и спрашивать: «Какой элемент ввода я хочу преобразовать в мой элемент вывода?»

Предполагая, что каждый ul должен содержать N item с, вы хотите преобразовать каждый N-й вход item, начиная с первого, в 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>

Каждый из этих item элементов становится ul, который содержит элемент и каждый из его N-1 следующих братьев и сестер:

<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>

Предполагая, что входной документ выглядит так:

<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>

... вы получите этот вывод, если для $n установлено значение 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>
1 голос
/ 09 октября 2009

Вы можете сделать это, используя рекурсивное решение:

<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>

Это называется шаблоном group для всего списка элементов. Шаблон group записывает первые три элемента списка (или меньше, если их не три) внутри тегов <ul> ... </ul>. Затем он снова вызывает себя, а остальная часть списка пропускает первые три. Когда список пуст, шаблон group ничего не делает.

XSLT - очень функциональный язык, и следование правилам (т. Е. Не использовать для этого disable-output-escaping) избавит вас от больших болей и страданий в будущем, когда вам потребуется снова изменить шаблоны.

0 голосов
/ 09 октября 2009

вы можете попробовать что-то вроде этого (не проверено, но у вас есть идея)

(...)
<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>
0 голосов
/ 09 октября 2009

вы можете сделать

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

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

так в вашем случае:

<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>

Обновление: Я удалил это после прочтения ответов Пьера и Грега, но я решил сохранить его, так как он отвечает на ваш вопрос, и это может оказаться полезным для кого-то где-то.

Обновление 2: Да, я понимаю, почему это может быть ужасно и внушает страх, похожий на Назгул, у моих сверстников, и да, я сам пытался опровергнуть это, однако я думаю, что этот ответ может быть полезен в будущем.

...