Search

Hello,

I have a small question about XSLT and entries filtering, and am hoping that someone with more experience can point me in the right direction.

I'm trying to build a tree navigation for a collection of pages organized in chapters, using a jQuery plugin called simpleTreeMenu.

I got it to work nicely with static content, now I'm trying to make it dynamic and use data from my entries.

I have two sections, Docs and Chapters. Every entry in Docs is assigned to a Chapter via a select box. So the tree menu needs two levels:

  1. Chapters
  2. Doc pages in Chapter

I'm now half-way through, iterating through each chapter:

<xsl:template name="nav-docs">
    <ul class='tree'>
        <xsl:for-each select="chapters/entry">
            <li>
                <span>
                    <xsl:value-of select="title"/>
                </span>
                <ul>
                    <!-- list of doc pages in chapter -->
                    <li><a href='#'>...</a></li>
                    <li><a href='#'>...</a></li>
                </ul>
            </li>
        </xsl:for-each>
    </ul>
</xsl:template>

The part I'm having trouble with is the second iteration: listing all doc pages in each chapter (the commented spot). I tried listing all pages to start:

<xsl:for-each select="docs/entry"> 
    <li>
        <xsl:value-of select="title"/>
    </li>
</xsl:for-each>

...but that doesn't work, I get an empty list. (The next step would be to filter this list by chapter.)

I feel this should be fairly easy to do, but I'm struggling to find the correct syntax.

Any tips? Thanks in advance!

Hi gferreira. I suggest you post your XML here, too. You may already know this, of course, but you can see the XML by appending ?debug=xml to the end of the page URL when you are logged in.

Hi David, thank you for your reply.

I can see the full source data in the XML with ?debug, but I don't know how to “call” it into the menu with XSLT.

The XML of the sections looks like this:

<chapters>
    <section handle="chapters">Chapters</section>
    <entry>
        <title handle="chapter-1">Chapter 1</title>
    </entry>
    <entry>
        <title handle="chapter-2">Chapter 2</title>
    </entry>
</chapters>

<docs>
    <section handle="docs">Docs</section>
    <entry>
        <title handle="a-doc-page">A Doc Page</title>
        <body mode="formatted">...</body>
        <chapter>
            <item handle="chapter-1">Chapter 1</item>
        </chapter>
    </entry>
    <entry>
        <title handle="another-doc-page">Another Doc Page</title>
        <body mode="formatted">...</body>
        <chapter>
            <item handle="chapter-2">Chapter 2</item>
        </chapter>
   </entry>
</docs>

I'll keep on trying, and will post results here if I can get it to work.

@gferreira

First of all I suggest using matched templates when coding instead of for-each. It's kind-of good practice.

<xsl:template name="nav-docs">
    <ul class='tree'>
        <xsl:apply-templates select="/data/chapters/entry">
    </ul>
</xsl:template>

<xsl:template match="chapters/entry">
    <li>
        <span>
            <xsl:value-of select="title"/>
         </span>
        <ul>
            <xsl:apply-templates select="/data/docs/entry[ chapter/item/@id = current()/@id ]">
        </ul>
    </li>
</xsl:template>

<xsl:template match="docs/entry">
    <li>
        <a href="...">...</a>
    </li>
</xsl:template>

This

<xsl:apply-templates select="/data/docs/entry[ chapter/item/@id = current/@id ]">

matches each doc entry when it's chapter id is the same as current chapter id.

@vladG: Many thanks for your reply, this is exactly what I was looking for.

(I am coming from a Python background, so I am used to think in terms of for-eachs. Still trying to wrap my head around the XSLT way of doing things – your example was very helpful.)

I got it to work with a few changes:

<xsl:template name="nav-docs">
    <ul class='tree'>
        <xsl:apply-templates select="/data/chapters/entry" />
    </ul>
</xsl:template>

<xsl:template match="chapters/entry">
    <li>
        <span>
            <xsl:value-of select="title"/>
        </span>
        <ul>
            <xsl:apply-templates select="/data/docs/entry[ chapter/item/@handle = current()/title/@handle ]" />
        </ul>
    </li>
</xsl:template>

<xsl:template match="docs/entry">
    <li>
        <a href="#">
            <xsl:value-of select="title"/>
        </a>
    </li>
</xsl:template>

So, the matching was not between @ids, but between chapter/@handle (doc page) and title/@handle (chapter).

Cheers!

I had forgotten to add the URL to each doc page, so here's an update:

<xsl:template match="docs/entry">
    <li>
            <a>
                <xsl:attribute name="href">
                    <xsl:value-of select="concat($root, '/docs/', title/@handle)" />
                </xsl:attribute>
                <xsl:value-of select="title"/>
            </a>
    </li>
</xsl:template>

Is there a more elegant way of doing the concat() part? (For example, without hard-wiring the docs section?)

Next:

  • make the menu tree stay open for the current chapter when switching pages
  • add next and previous links for page navigation

Is there a more elegant way of doing the concat() part? (For example, without hard-wiring the docs section?)

Well, not really. That is the handle of your Docs Page in the end. If you need to rename the handle, you'll make a global search & replace in your stylesheets (.xsl files).

Perhaps you would use this to look prettier:

<a href="{$root}/docs/{title/@handle}">
    <xsl:value-of select="title"/>
</a>

Thanks vladG, that does look better (more Symphonic). ;-)

I am a bit baffled that your XML didn't contain any @ids. Did you strip them out? The reason I am asking is because you should use @ids rather than @handles to find related nodes. i.e.:

<xsl:apply-templates select="/data/docs/entry[chapter/item/@id = current()/title/@id]" />

Hi Nils, thanks for the tip.

Yes, I did leave out the ids in the XML samples. Now I know they are important. :-)

I tried using ids like in your example, but it doesn't work – I get an empty list.

Anything special I should be doing with the data sources?

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details