Рекурсивный список списков в XSL - PullRequest
1 голос
/ 23 февраля 2010

У меня есть рекурсивные узлы, которые я пытаюсь настроить для jquery-checktree. Узлы выглядят как

foo/bar/ID
       /NAME
       /CHECKED
       bar/ID
          /NAME
          /CHECKED
   /bar/ID
       /NAME
   /bar/ID
       /NAME
       /bar/ID
           /NAME
           /CHECKED
           /bar/ID
               /NAME
               /CHECKED

Там, где любой бар может иметь или не иметь один или несколько узлов баров под ним, но любой бар будет иметь ID и ИМЯ и может иметь ПРОВЕРКУ.

и я хочу превратить это в

<ul>
  <li><input type="checkbox" name="..." value="..." checked="checked"></input>
      <label for="...">...</label>
      <ul>
        <li><input type="checkbox" name="..." value="..." checked="checked"></input>
          <label for="...">...</label>
        </li>
      </ul>
  <li>....</li>
</ul>

Я могу получить первый уровень, выполнив:

    <ul class="tree">
    <xsl:for-each select="/foo/bar/">
        <li><input type="checkbox" name="{ID}" value="{ID}">
            <xsl:if test="CHECKED = 'Y'"><xsl:attribute name="checked">checked</xsl:attribute></xsl:if>
            </input><label for="{ID}"><xsl:value-of select="NAME"/></label>
        </li>
    </xsl:for-each>
    </ul>

Но я не знаю, как вернуться к встроенному «бару» внутри «бара», вплоть до скольких уровней может быть.

Ответы [ 3 ]

5 голосов
/ 23 февраля 2010

Вот один из способов:

<xsl:template match="bar">
    <li>
        <input type="checkbox" name="{ID}" value="{ID}">
            <xsl:if test="CHECKED = 'Y'">
                <xsl:attribute name="checked">checked</xsl:attribute>
            </xsl:if>
        </input>
        <label for="{ID}"><xsl:value-of select="NAME"/></label>
        <!-- 

            If we have bar children, make a list and recurse

        -->
        <xsl:if test="bar">
            <ul>
                <xsl:apply-templates select="bar"/>
            </ul>
        </xsl:if>
    </li>
</xsl:template>

Это зависит от "автоматического" сопоставления шаблона. Чтобы обеспечить соответствие, вы можете либо поместить <xsl:apply-templates/> в цикл <xsl:for-each> вашего исходного кода, однако вы можете даже улучшить все это и заменить исходный код следующим шаблоном:

<xsl:template match="/foo">
   <ul class="tree">
       <xsl:apply-templates select="bar"/>
   </ul>
</xsl:template>

Если вы хотите больше контроля, вы также можете использовать <xsl:for-each select="bar"> и вызывать именованный шаблон (<xsl:template name="some-name">... и <xsl:call-template>) внутри цикла. Смотри: http://www.w3.org/TR/xslt#named-templates

3 голосов
/ 23 февраля 2010

Это пример (на самом деле подтверждение концепции) для полностью ориентированного на ввод решения в стиле push (только сопоставление с шаблоном, без условий, без именованных шаблонов):

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
>

  <xsl:template match="*[bar]">
    <ul class="tree">
      <xsl:apply-templates select="bar" mode="li" />
    </ul>
  </xsl:template>

  <xsl:template match="bar" mode="li">
    <li>
      <xsl:apply-templates select="." mode="checkbox" />
      <xsl:apply-templates select="(.)[bar]" />
    </li>
  </xsl:template>

  <xsl:template match="bar" mode="checkbox">
    <input type="checkbox" id="{ID}" name="{NAME}">
      <xsl:apply-templates select="CHECKED" />
    </input>
    <label for="{ID}">
      <xsl:value-of select="NAME" />
    </label>
  </xsl:template>

  <xsl:template match="CHECKED">
    <xsl:attribute name="checked">checked</xsl:attribute>
  </xsl:template>

</xsl:stylesheet>

Применительно к этому входному XML (экстраполирован из вашего вопроса):

<foo>
  <bar>
    <ID>nd1</ID>
    <NAME>Node 1</NAME>
    <CHECKED />
    <bar>
      <ID>nd2</ID>
      <NAME>Node 2</NAME>
      <CHECKED />
    </bar>
  </bar>
  <bar>
    <ID>nd3</ID>
    <NAME>Node 3</NAME>
  </bar>
  <bar>
    <ID>nd4</ID>
    <NAME>Node 4</NAME>
    <bar>
      <ID>nd5</ID>
      <NAME>Node 5</NAME>
      <CHECKED />
      <bar>
        <ID>nd6</ID>
        <NAME>Node 6</NAME>
        <CHECKED />
      </bar>
    </bar>
  </bar>
</foo>

Он производит такой вывод:

<ul class="tree">
  <li>
    <input type="checkbox" id="nd1" name="Node 1" checked="checked" />
    <label for="nd1">Node 1</label>
    <ul class="tree">
      <li>
        <input type="checkbox" id="nd2" name="Node 2" checked="checked" />
        <label for="nd2">Node 2</label>
      </li>
    </ul>
  </li>
  <li>
    <input type="checkbox" id="nd3" name="Node 3" />
    <label for="nd3">Node 3</label>
  </li>
  <li>
    <input type="checkbox" id="nd4" name="Node 4" />
    <label for="nd4">Node 4</label>
    <ul class="tree">
      <li>
        <input type="checkbox" id="nd5" name="Node 5" checked="checked" />
        <label for="nd5">Node 5</label>
        <ul class="tree">
          <li>
            <input type="checkbox" id="nd6" name="Node 6" checked="checked" />
            <label for="nd6">Node 6</label>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
2 голосов
/ 23 февраля 2010

<xsl:template match="foo">
    <ul class="tree">
        <xsl:apply-templates/>
    </ul>
</xsl:template>

<xsl:template match="bar" name="wunderbar">
<!-- we want to match all bars, not only /foo/bars -->
    <li>
        <input type="checkbox" name="{ID}" value="{ID}">
        <xsl:if test="CHECKED = 'Y'"><xsl:attribute name="checked">checked</xsl:attribute></xsl:if>
        </input><label for="{ID}">
            <xsl:value-of select="NAME"/>
        </label>
        <!-- If there is some bar, the next template is applied -->
        <xsl:apply-templates/>
    </li>
</xsl:template>

<xsl:template match="bar/bar">
<!-- Just adds <ul> around bar included in bar and calls the usual template -->
    <ul>
        <xsl:call-template name="wunderbar"/>
    </ul>
</xsl:template>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...