0

我正在尝试构建一个理论上可以使用 XSL 无限递归的导航。不幸的是,我在这个领域的技能仍在培养中。谁能告诉我这段代码哪里出错了?

这里的关键是导航的前 2 级有些独特(类名等),但在第 3 级之后,导航几乎是一遍又一遍嵌套的相同元素。

更新: 在导航级别 2 之后没有递归,在级别 2 之后输出不存在,我不知道如何应用递归。我觉得这应该更容易,但我在 XSL 方面的技能并不是那么好。

HTML(我们需要它来生成):

  <ul class="nav-l1">
    <li class="nav-active"><a href="nav2.html" class="nav-item trigger-chan nav-next"><span><span class="trigger-cntr">First Level Parent</span></span></a>
      <ul class="nav-l2 nav-hidden">

        <li class="nav-active"><a href="nav2.html" class="nav-item"><span>Overview</span></a></li>
        <li><a href="#" class="nav-item trigger-chan nav-next"><span><span class="trigger-cntr">Second Level Parent</span></span></a>

          <ul class="nav-ls nav-hidden">
            <li><a href="#" class="nav-item"><span>Third Level</span></a></li>
            <li><a href="#" class="nav-item"><span>Third Level</span></a></li>
          </ul>
        </li>
        <li><a href="#" class="nav-item"><span>Second Level</span></a></li>
      </ul>
    </li>

    <li><a href="#" class="nav-item trigger-chan nav-next"><span><span class="trigger-cntr">First Level Parent</span></span></a>

      <!-- 2nd level of navigation.  -->
      <ul class="nav-l2 nav-hidden">
        <li><a href="#" class="nav-item"><span>Overview</span></a></li>
        <li><a href="#" class="nav-item trigger-chan nav-next"><span><span class="trigger-cntr">Second Level Parent</span></span></a>

          <!-- 3rd level of navigation.  -->
          <ul class="nav-ls nav-hidden">
            <li><a href="#" class="nav-item"><span>Third Level</span></a></li>
            <li><a href="#" class="nav-item"><span>Third Level</span></a></li>
            <li><a href="#" class="nav-item"><span>Third Level</span></a></li>
          </ul>
        </li>
        <li><a href="#" class="nav-item"><span>Second Level</span></a></li>
        <li><a href="#" class="nav-item"><span>Second Level</span></a></li>
      </ul>
    </li>

  </ul>

示例 XML(初始数据馈送的格式):

<data>
    <folders level="1">
        <folder clickable="Y" url="/lorem/ipsum.html" name="Lorem Ipsum"/>
        <folder clickable="Y" url="/level/one.html" name="Level One"/>
        <folder clickable="Y" url="/foo/bar.html" name="Foo Bar">
            <folders level="2">
                <folder clickable="Y" url="/level/two.html" name="Level two"/>
                <folder clickable="Y" url="/child/item.html" name="Child Item">
                    <folders level="3">
                        <folder clickable="Y" url="/child/child/item.html" name="Child's Child Item">
                            <folders level="4">
                                <folder clickable="Y" url="/destiny/child/item.html" name="Destiny's Child"/>
                            </folders>
                        </folder>
                    </folders>
                </folder>
            </folders>
        </folder>
    </folders>
</data>

XSL(我们需要转换 XML):

    <?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="/">
    <xsl:choose>
      <xsl:when test="/data/folders">

          <!-- NAVIGATION BEGINS HERE -->
          <ul class="nav-l1">

            <!-- LEVEL 1 -->
            <xsl:for-each select="/data/folders[@level=1]/folder[not (@clickable ) or @clickable ='Y' ]">
              <li>
                <xsl:if test="@selected='Y'">
                  <xsl:attribute name="class">nav-active</xsl:attribute>
                </xsl:if>

                <a href="{@url}" class="nav-item">
                  <xsl:choose>
                    <xsl:when test="child::*">
                      <xsl:attribute name="class">trigger-chan nav-next</xsl:attribute>
                      <span>
                            <span class="trigger-cntr">
                              <xsl:value-of select="@name"/>
                            </span>
                      </span>
                    </xsl:when>
                    <xsl:otherwise>
                      <span><xsl:value-of select="@name" /></span>
                    </xsl:otherwise>
                  </xsl:choose>
                </a>

                <!-- LEVEL 2 -->
                <xsl:choose>
                  <xsl:when test="child::*">
                    <ul class="nav-l2 nav-hidden">
                      <xsl:for-each select="folders[@level=2]/folder[not (@clickable ) or @clickable ='Y' ]">
                        <li>
                          <xsl:if test="@selected='Y'">
                            <xsl:attribute name="class">nav-active</xsl:attribute>
                          </xsl:if>

                          <a href="{@url}" class="nav-item">
                            <xsl:choose>
                              <xsl:when test="child::*">
                                <xsl:attribute name="class">trigger-chan nav-next</xsl:attribute>
                                <span>
                                <span class="trigger-cntr">
                                  <xsl:value-of select="@name"/>
                                </span>
                              </span>
                              </xsl:when>
                              <xsl:otherwise>
                                <span><xsl:value-of select="@name" /></span>
                              </xsl:otherwise>
                            </xsl:choose>
                          </a>

                          <!-- LEVEL 3 and beyond ... -->
                          <xsl:apply-templates />

                        </li>
                      </xsl:for-each>
                    </ul>
                  </xsl:when>
                  <xsl:otherwise>
                  </xsl:otherwise>
                </xsl:choose>
              </li>

            </xsl:for-each>
          </ul>
      </xsl:when>
    </xsl:choose>
  </xsl:template>

<!-- Infinity and beyond (=> Level 3) -->
  <xsl:template name="folder">
    <ul class="nav-ls nav-hidden">
      <li>
          <xsl:if test="@selected='Y'">
            <xsl:attribute name="class">nav-active</xsl:attribute>
          </xsl:if>

          <a href="{@url}" class="nav-item">
            <xsl:choose>
              <xsl:when test="child::*">
                <xsl:attribute name="class">trigger-chan nav-next</xsl:attribute>
            <span>
                      <span class="trigger-cntr">
                        <xsl:value-of select="@name"/>
                      </span>
                </span>
              </xsl:when>
              <xsl:otherwise>
                <span><xsl:value-of select="@name" /></span>
              </xsl:otherwise>
            </xsl:choose>
          </a>
        <xsl:apply-templates select="folders[@level>3]/folder" />
      </li>
    </ul>
  </xsl:template>

</xsl:stylesheet>

任何帮助是极大的赞赏。

谢谢!

4

1 回答 1

3

您的 XSLT 非常注重“拉”而不是“推”,并且可以使用一些重新设计来使其功能更强大(在函数式编程意义上)。

但主要错误在这里:

<!-- Infinity and beyond (=> Level 3) -->
<xsl:template name="folder">

这是声明一个“命名模板”,但它从未被调用过。这不是一件坏事,因为应尽可能避免使用名称/呼叫模板。

将其更改为以下内容,当使用apply-templates您在 XSLT 中已有的内容时,它应该可以按预期工作。

<xsl:template match="folder">

编辑:推式方法

<folders>这可能并不完美,但有几件事: *当它们是 list时,您会完全跳过,而<folder>s 是列表项。* 你似乎重复了 1、2 和 3 级的项目代码。 * 你只在 3 级及以上制作了一个项目,所有其他项目都会被忽略?

请注意下面,我已经定义了我希望如何<folders>渲染,基于级别,以及如何<folder>独立渲染 s。

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
    <xsl:apply-templates/>
</xsl:template>
<xsl:template match="folders">
    <ul>
        <xsl:attribute name="class">
            <xsl:text>nav-l</xsl:text>
            <xsl:choose>
                <xsl:when test="@level&gt;2">
                    <xsl:text>s nav-hidden</xsl:text>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="@level"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:attribute>
        <xsl:apply-templates/>
    </ul>
</xsl:template>
<xsl:template match="folder">
    <li>
        <xsl:if test="@selected='Y'">
            <xsl:attribute name="class">nav-active</xsl:attribute>
        </xsl:if>
        <a href="{@url}" class="nav-item">
            <xsl:choose>
                <xsl:when test="child::*">
                    <xsl:attribute name="class">trigger-chan nav-next</xsl:attribute>
                    <span>
                        <span class="trigger-cntr">
                            <xsl:value-of select="@name"/>
                        </span>
                    </span>
                </xsl:when>
                <xsl:otherwise>
                    <span>
                        <xsl:value-of select="@name"/>
                    </span>
                </xsl:otherwise>
            </xsl:choose>
        </a>
        <xsl:apply-templates select="folders"/>
    </li>
</xsl:template>
</xsl:stylesheet>

注意上面的模板比你的初始模板短,因为重复的元素被删除了。所有<folder>s 渲染相同,因此只需要一个模板。事实上,<folder>s 不需要知道他们的级别,只有父级知道<folders>,这样逻辑就会移动到正确的模板中。

不熟悉 XSLT 的人经常忽略如何apply-templates有效地使用它来定义递归并允许元素之间非常清晰的分离。在创建模板时,尝试并关注应该如何表示这个特定元素,并了解它的子元素可能是如何被需要的。然后,专注于子元素的新模板以及应该如何呈现这些模板等等。

除上述内容外,请始终注意../in @selects 或<value-of>s ,因为这意味着您的逻辑依赖于当前“范围”之外的元素。并不总是坏事,但要注意一些事情。

在可能的情况下尝试并最小化for-each它们,看看它们是否可以被重写以使用apply-templates,除非出于某种奇怪的原因绝对需要,否则尽量不要使用call-templates它们,因为它们的功能性较差。

于 2013-10-31T23:19:23.260 回答