MODX Menu Section

Jul 26, 2017

Requirement

If the current Resource is in a certain "section" of the site, that section's menu item gets an "active" class.

We've all seen this requirement a million times, and there's nearly as many ways to solve for it. Almost all of them involve comparing some value that's available on the menu item Resource, with some value available on the current Resource. For example, the menu item Snippet TPL might be something like:

<li [[UltimateParent:is=`[[+id]]`:then=`class="active"`:else=``]]>
    <a href="[[~[[+id]]]]">[[+pagetitle]]</a>
</li>

Here are a few gotchas I've come across:

  • How do you define "section"? It's probably a subset of Resources—let's call them "Section Resources".
  • Sometimes the Section Resources don't all just co-operatively reside at the top of the Resource Tree, so the default UltimateParent call won't work.
  • The UltimeParent Snippet has the ability to set the "top level" as something other than the very top, but sometimes the Section Resources aren't at the same level of the Tree. This means that each Section Resource's menu item would need different properties passed to UltimateParent. Painful.

Approach #1

The de-facto solution for more complex scenarios like the above, is to use a TV with the default value set to @INHERIT.

Then you'd set a value in the Section Resource—ideally something that's available on the Resource object like the "alias" or "id". All offspring, at any level, would "inherit" that value. Also at any level, you could override it, and at that point subsequent offspring would inherit the new value. You can check for the inherited value in the TV, and compare against the Section Resource menu item, like this:

<li [[*section:is=`[[+alias]]`:then=`class="active"`:else=``]]>
    <a href="[[~[[+id]]]]">[[+pagetitle]]</a>
</li>

See how that works? If the "section" TV on the current Resource has the same value as the "alias" of the menu item Resource, then the current Resource is "in that section" (a descendant of that menu item Resource).

It works well. Sometimes it can get a bit confusing to know which Resources are "Section Resources" (by way of having a unique value in the TV), because you can't quickly inspect TV values in the Tree. The Collections Extra has a way to display TV values in a grid view of Resources. It's neat-o.

Approach #2

If for some reason you don't want to use a TV, you could use a custom Snippet that handles the Section Resource logic:

  1. You provide it with the IDs of the Section Resources
  2. It fetches the IDs of the current Resource's parents (all the way up the tree)
  3. You provide it with a "test case" ID, presumably the menu item Resource's ID
  4. It takes the intersection of parent IDs and Section Resource IDs to determine if the current Resource is a descendant of any of the Section Resources.
  5. If it is, and the menu item Resource's ID matches, we have a winner!

Here's a gist:

Note: the Snippet example doesn't handle the case where the Resource is a descendant of more than one Section Resource. If you have more than one matching Section Resource in the menu, both would get an "active" class. With a bit more code you could specify some logic to determine which Section Resource takes precendence, in such cases.

Let me reiterate that the TV approach would be just fine and dandy for the vast majority of use cases. If you're like me and you like to have alternatives, this one might be helpful :)