How-to: Use MODX getResources for Dynamic Nested Menus

Jul 4, 2013

It's All About the Wrapper

Until the release of getResources version 1.6.0 ("gR"), it wasn't practical to use this multi-purpose snippet for menu-building. That's all changed now with the tplWrapper and wrapIfEmpty properties. If tplWrapper is set, gR will send the output to a placeholder and wrap it with the contents of the specified tpl Chunk. If there's no output, the wrapper won't be rendered either. The following is a tutorial on how to use getResources for a multi-level, nested menu.

Resources

The Snippet Call

[[getResources@main-menu?parents=`-1`&resources=`2,3,4,5,6`]] 

Break it down:

  • @main-menu » This calls a property set with all the magic stuff. I'll go into detail on that below.
  • parents=`-1` » When you want to list top level Resources, you must disable the parents property and use the resources property. This is the one drawback of using getResources for menus over the Wayfinder snippet. A feature request has been submitted here.
  • resources=`2,3...` » So instead you specify a comma separated list of Resource IDs to list, and depending on the setup, their child Resources will be listed as well.

Let's take a look at the property set. In case you don't know, property sets are simply a way to call several properties at once, keeping the properties organized and your code cleaner. You define property sets in the "Properties" tab of the Snippet Edit View (click on the Snippet's name in the Elements tab).

blog-files/Screen Shot 2013-07-04 at 3.25.30 PM.png

Here are the properties defined in our main-menu property set:

  • depth=`0` » This is important. We're only going down one level, and if certain conditions are met, gR is called again to list the next level. More details below in the section on the tpl Chunks.
  • level=`1` » Can you feel chills down your spine from all the magic? This is a custom property that we define using the "Create Property" button. You'll see what it does later, but the value would almost always be set as `1`.
  • maxLevel=`2` » Another custom property to set the maximum level gR will traverse down from the parents/top-level Resources.
  • sortby=`menuindex` & sortdir=`ASC` » So the menu will render in the same order as the MODX Resource Tree. This is optional but generally desired.
  • tpl=`myRowTpl` » There's more magic in this tpl Chunk, described below. If you want submenus to use a different Chunk, you can do that via another property set. See below.
  • tplWrapper=`myWrapperTpl` » Simple html wrapper and the placeholder.

Here's the contents of the myWrapperTpl chunk:

<ul[[+outerClasses:notempty=` class="[[+outerClasses]]"`]]> [[+output]]</ul>

Break it down:

We can pass the outerClasses property to the chunk in gR's property set or the snippet call itself to add classes to the wrapper element, just like Wayfinder. The placeholder is where gR returns the results of the query. Next here's the contents of the myRowTpl chunk:

<li[[*id:is=`[[+id]]`:then=` class="active"`]]>
    <a href="[[~[[+id]]]]">
        [[+menutitle:default=`[[+pagetitle]]`]]
    </a>
    [[[[+level:lt=`[[+maxLevel]]`:then=`getResources@main-menu? &parents=`[[+id]]` &level=`[[+level:add]]``:else=`-`]]]]
    </li> 

Break it down:

Don't fall down off your seat just yet LOL. We'll go through this line by line in pseudo code, k?

  • List element. If the ID of the current Resource is the same as that of the Resource being returned, then add the `class="active"` attribute.
  • Make an anchor tag with MODX's link tag syntax.
  • As anchor text use the Resource's menutitle field, unless it's empty, then use the pagetitle.
  • Now MODX is going to process something. If the value in the [[+level]] placeholder is less than the value in the [[+maxLevel]] placeholder, then output the text (which is another gR snippet call).
  • When MODX outputs this text inside the outer set of double square brackets, the result is a call to gR again with our custom property set, the parents property being populated with the ID of the Resource currently returned, and the [[+level]] property increments by one and is passed to the tpl Chunk.
  • [[+level]] acts as an indicator of how deep you are from the top-level because every time gR is called using this Chunk, it is returning children one level deeper and incrementing the value in [[+level]].
  • [[+maxLevel]] is set once in the property set or Snippet call, and it never increments. So if you don't want gR to go more than 2 levels deep for the menu, set [[+maxLevel]] to 2 and the condition will evaluate false at that point and return a hyphen: :else=`-`. The result is a MODX comment tag [[-]] that outputs nothing.

Whoa nelly, we just did a lot with that Chunk. It takes care of the recursion, listing children deeper and deeper until the maxLevel is reached. We now have a nested menu using gR instead of Wayfinder. You can change the tpl Chunk used for each level, or any of the other properties for that matter. So the top-level call could use myOuterRowTpl and the Snippet call therein would use a different Chunk, like myInnerRowTpl, which could in turn call gR with yet another different Chunk, with a different sort order, etc. It's very, very flexible, but of course be careful not to tax your PHP processes and Always Be Caching.

That's it! You now have one less package to install and maintain in your MODX site :)