Pregunta

Estoy tratando de crear un menú CSS de varios niveles para un sitio web que estoy haciendo en el sistema de administración de contenido de umbraco.

Necesito construirlo para tener la siguiente estructura:

<ul id="nav">
  <li><a href="..">Page #1</a></li>
  <li>
    <a href="..">Page #2</a>
    <ul>
      <li><a href="..">Subpage #1</a></li>
      <li><a href="..">Subpage #2</a></li>        
    </ul>
  </li>
</ul>

Así que ahora estoy tratando de descubrir cómo anidar usando XSLT. Esto es lo que tengo hasta ahora:

<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>

<!-- update this variable on how deep your menu should be -->
<xsl:variable name="maxLevelForMenu" select="4"/>

<xsl:template match="/">
  <ul id="nav">
    <xsl:call-template name="drawNodes">  
      <xsl:with-param 
       name="parent" 
       select="$currentPage/ancestor-or-self::node [@level=1]"
      />  
    </xsl:call-template>
  </ul>
</xsl:template>

<xsl:template name="drawNodes">
  <xsl:param name="parent"/> 
  <xsl:if test="umbraco.library:IsProtected($parent/@id, $parent/@path) = 0 or (umbraco.library:IsProtected($parent/@id, $parent/@path) = 1 and umbraco.library:IsLoggedOn() = 1)">
    <xsl:for-each select="$parent/node [string(./data [@alias='umbracoNaviHide']) != '1' and @level &lt;= $maxLevelForMenu]"> 
      <li>
        <a href="{umbraco.library:NiceUrl(@id)}">
          <xsl:value-of select="@nodeName"/>
        </a>  
        <xsl:if test="count(./node [string(./data [@alias='umbracoNaviHide']) != '1' and @level &lt;= $maxLevelForMenu]) &gt; 0">   
          <xsl:call-template name="drawNodes">    
            <xsl:with-param name="parent" select="."/>    
          </xsl:call-template>  
        </xsl:if> 
      </li>
    </xsl:for-each>
  </xsl:if>
</xsl:template>

Lo que parece que no puedo entender es cómo verificar si el primer nivel (aquí Página # 1 y Página # 2) tiene hijos, y si agregan el <ul> adicional para contener el <li> niños.

¿Alguien por ahí para señalarme en la dirección correcta?

¿Fue útil?

Solución

Primero, no es necesario pasar el parámetro a parent. El contexto transportará esta información.

Aquí está la hoja de estilo XSL que debería resolver su problema:

<!-- update this variable on how deep your menu should be -->
<xsl:variable name="maxLevelForMenu" select="4"/>

<!--- match the document root --->
<xsl:template match="/root">
  <div id="nav">
    <xsl:call-template name="SubTree" />
  </div>
</xsl:template>

<!-- this will be called by xsl:apply-templates -->
<xsl:template match="node">
  <!-- the node is either protected, or the user is logged on (no need to check for IsProtected twice) -->
  <xsl:if test="umbraco.library:IsProtected($parent/@id, $parent/@path) = 0 or umbraco.library:IsLoggedOn() = 1">
    <li>
      <a href="{umbraco.library:NiceUrl(@id)}"><xsl:value-of select="@nodeName"/></a>
      <xsl:call-template name="SubTree" />
    </li>
  </xsl:if>
</xsl:template>

<xsl:template name="SubTree">
  <!-- render sub-tree only if there are any child nodes --->
  <xsl:if test="node">
    <ul>
      <xsl:apply-templates select="node[data[@alias='umbracoNaviHide'] != '1'][@level &lt;= $maxLevelForMenu]">
        <!-- ensure sorted output of the child nodes --->
        <xsl:sort select="@sortOrder" data-type="number" />
      </xsl:apply-templates>
    </ul>
  </xsl:if>
</xsl:template>

Este es el XML en el que lo probé (no sé mucho sobre Umbraco, pero después de mirar algunas muestras espero haberme acercado a un documento de Umbraco):

<root id="-1">
  <node id="1" level="1" sortOrder="1" nodeName="Page #1">
    <data alias="umbracoNaviHide">0</data>
  </node>
  <node id="2" level="1" sortOrder="2" nodeName="Page #2">
    <data alias="umbracoNaviHide">0</data>
    <node id="3" level="2" sortOrder="2" nodeName="Subpage #2.2">
      <data alias="umbracoNaviHide">0</data>
    </node>
    <node id="4" level="2" sortOrder="1" nodeName="Subpage #2.1">
      <data alias="umbracoNaviHide">0</data>
      <node id="5" level="3" sortOrder="3" nodeName="Subpage #2.1.1">
        <data alias="umbracoNaviHide">0</data>
      </node>
    </node>
    <node id="6" level="2" sortOrder="3" nodeName="Subpage #2.3">
      <data alias="umbracoNaviHide">1</data>
    </node>
  </node>
  <node id="7" level="1" sortOrder="3" nodeName="Page #3">
    <data alias="umbracoNaviHide">1</data>
  </node>
</root>

Esta es la salida:

<div id="nav">
  <ul>
    <li><a href="http://foo/">Page #1</a></li>
    <li><a href="http://foo/">Page #2</a>
      <ul>
        <li><a href="http://foo/">Subpage #2.1</a>
          <ul>
            <li><a href="http://foo/">Subpage #2.1.1</a></li>
          </ul>
        </li>
        <li><a href="http://foo/">Subpage #2.2</a></li>
      </ul>
    </li>
  </ul>
</div>

Otros consejos

No hay nada muy especial sobre este problema. La siguiente solución prueba que la lista de nodos para

<xsl:apply-templates/>

no está vacío, antes de aplicar las plantillas:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes"/>
  <xsl:variable name="vLevel" select="0"/>

    <xsl:template match="root">
      <xsl:variable name="vnextLevelNodes"
           select="node[@level = $vLevel+1]"/>
      <xsl:if test="$vnextLevelNodes">
       <ul>
         <xsl:apply-templates select="$vnextLevelNodes"/>
       </ul>
      </xsl:if>
    </xsl:template>

    <xsl:template match="node">
  <!-- the node is either protected, or the user is logged on (no need to check for IsProtected twice) -->
    <!-- <xsl:if test=
      "umbraco.library:IsProtected($parent/@id, $parent/@path) = 0
      or
       umbraco.library:IsLoggedOn() = 1"> -->
    <xsl:if test="1">
        <li>
          <!-- <a href="{umbraco.library:NiceUrl(@id)}"> -->
          <a href="'umbraco.library:NiceUrl(@id)'">
            <xsl:value-of select="@nodeName"/>
          </a>

                  <xsl:variable name="vnextLevelNodes"
                       select="node[@level = current()/@level+1]"/>
                  <xsl:if test="$vnextLevelNodes">
                   <ul>
                     <xsl:apply-templates select="$vnextLevelNodes"/>
                   </ul>
                  </xsl:if>
        </li>
    </xsl:if>
    </xsl:template>
</xsl:stylesheet>

He usado el siguiente documento fuente XML:

<root id="-1">
    <node id="1" level="1" sortOrder="1" nodeName="Page #1">
        <data alias="umbracoNaviHide">0</data>
    </node>
    <node id="2" level="1" sortOrder="2" nodeName="Page #2">
        <data alias="umbracoNaviHide">0</data>
        <node id="3" level="2" sortOrder="2" nodeName="Subpage #2.2">
            <data alias="umbracoNaviHide">0</data>
        </node>
        <node id="4" level="2" sortOrder="1" nodeName="Subpage #2.1">
            <data alias="umbracoNaviHide">0</data>
            <node id="5" level="3" sortOrder="3" nodeName="Subpage #2.1.1">
                <data alias="umbracoNaviHide">0</data>
            </node>
        </node>
        <node id="6" level="2" sortOrder="3" nodeName="Subpage #2.3">
            <data alias="umbracoNaviHide">1</data>
        </node>
    </node>
    <node id="7" level="1" sortOrder="3" nodeName="Page #3">
        <data alias="umbracoNaviHide">1</data>
    </node>
</root>

Además, he comentado cualquier código que haga referencia a las funciones de extensión de Umbraco, ya que no tengo acceso a ellas.

Cuando la transformación anterior se aplica en este documento XML de origen, se produce el resultado deseado correcto:

<ul>
    <li>
        <a href="'umbraco.library:NiceUrl(@id)'">Page #1</a>
    </li>
    <li>
        <a href="'umbraco.library:NiceUrl(@id)'">Page #2</a>
        <ul>
            <li>
                <a href="'umbraco.library:NiceUrl(@id)'">Subpage #2.2</a>
            </li>
            <li>
                <a href="'umbraco.library:NiceUrl(@id)'">Subpage #2.1</a>
                <ul>
                    <li>
                        <a href="'umbraco.library:NiceUrl(@id)'">Subpage #2.1.1</a>
                    </li>
                </ul>
            </li>
            <li>
                <a href="'umbraco.library:NiceUrl(@id)'">Subpage #2.3</a>
            </li>
        </ul>
    </li>
    <li>
        <a href="'umbraco.library:NiceUrl(@id)'">Page #3</a>
    </li>
</ul>

Espero que esto haya ayudado.

Saludos,

Dimitre Novatchev

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top