Mega-menus the mega-awesome way: with MODX

Jan 25, 2017

Mega-menus the mega-awesome way: with MODX

If you're reading this, you're probably familiar with the mega-menu design pattern. It can be a useful and aesthetic way to present navigation, alongside featured content and CTAs.

In MODX, you often build menus dynamically from the hierarchical Resource Tree. If each Resource represents a menu item, it follows that each mega-menu also corresponds to a Resource.

So today's topic: how to manage mega-menu content with Resources?

Note: MODX doesn't require you to relate menus to Resources. It's a common pattern, but that, by itself, isn't a good reason to do it. It's possible there's a more suitable approach for your project, in which case this post would be irrelevant ;)

The Basic Idea

  • The Resources representing mega-menu items will use a special Template.
  • We'll use the getResources Snippet to conditionally render mega-menu markup for those Resources, and regular menu item markup for all others.
  • This gives us a few UI options for managing mega-menu content, with varying degrees of automation and flexibility. We should be able to meet most use cases with this approach.

Resource Tree
The Resource Tree has customized icons for Resources using the special mega-menu Template

Listing Snippet

I find getResources much more flexible than Wayfinder for building menus, or rendering any collection of resources, for that matter.

It features a tplCondition property, wherein you specify a Resource field, the value of which will trigger a special template Chunk to be used in rendering that Resource. For our purposes we'll use the template field.

Here's an example Snippet call. Only the properties related to the output templates are shown. You'll want to add the regular getResources properties like &parents, &limit, &depth, etc.

[[getResources?
    &tpl=`menu-item-tpl`
    &tplCondition=`template`
    &conditionalTpls=`{"5":"mega-menu-tpl"}`
]]

Break it down:

  • tpl - this defines the default tpl Chunk to use for standard menu items
  • tplCondition - the Resource field to "watch" for
  • conditionalTpls - a JSON object where the key of each object property corresponds to a value in the "watched" Resource field, and the value of the object property is the name of the template Chunk to use, in that condition.

The Snippet properties above mean, in plain English: "if a Resource is assigned the Template with ID 5, use the Chunk mega-menu-tpl to render it, otherwise use the Chunk menu-item-tpl".

Content Management

The following options (and more) are available for managing mega-menu content when using this approach.

The last option is my personal favourite. It utilizes the ContentBlocks premium Extra to provide maximum control to content editors—and it's the most performant.

For our example use case, we'll assume that the mega-menu items contain 3 child elements in a dropdown:

  1. An unordered list of (Resource) links
  2. A Featured Resource with image, headline, and excerpt, which all link to the Resource
  3. An announcement or CTA with button and optional image or callout text.

As with any MODX-powered front end, you can present these elements however you wish—with any framework, or none at all. I use a 3-column Bootstrap dropdown, in this example, and each of the above elements render in its own column.

Mega Menu common design pattern

Fully-automated approach: slowest with least flexibility

The mega-menu template Chunk would contain 3 getResources calls, one for each column/element.

The list of Resource links would be a standard getResources call.

The Featured Resource would be a getResources call returning one result, sorted by either: a field like publishedon, or a dedicated TV that serves as a flag. getResources supports sorting and filtering by TV values, but it can be somewhat expensive.

The CTA would be managed with a Weblink Resource, also fetched by getResources.

This option is automated, in that when Resources are edited and their field or TV values saved, the menu would dynamically repopulate with any relevant changes. However it's the most draining on server resources.

Middle-of-the-road approach

This time the mega-menu template Chunk could get be rendered with 2 getResources calls.

The list of Resource links would be the same as above.

For the Featured Resource and CTA, a getResources call would fetch a single, known Resource by ID, like [[++site_start]].

This "menu config" Resource's TV values would be used to render both mega-menu elements. For example, a content editor would select the Featured Resource using a Resource list TV. Other TVs would be used to manage the CTA image, CTA link and CTA text, etc. The template Chunk would render two columns instead of one.

This approach should have better performance, because you only fetch the one "menu config" Resource, rather than the above where we sort through all Resources—twice— to find the matching Featured and CTA Resources.

It's also a bit more flexible in that a content editor can choose a Featured Resource that hasn't been specifically flagged, and no Weblink Resource needs to be created in order for the CTA to link externally.

Fastest and most flexible approach

This is my favourite, because it handles almost all processing when the content editor saves a Resource, rather than when the menu is rendered—which usually happens on every page of the site.

The mega-menu template Chunk contains no Snippet calls, but only the [[+content]] placeholder. The mega-menu Resource is assigned to a special Template, is ContentBlocks (CB) enabled, and has access to a special CB layout and fields for managing our 3-column mega-menu.

ContentBlocks

Actually, in this approach, the content editor is free to put the mega-menu elements in whichever column they want, or even omit some of the elements, allowing the use of the mega-menu presentation without imposing the 3-column configuration. Any combination of CB fields you enable, can be rendered here.

2 column example

The best part is that, any template conditionals get parsed into the Resource content field on save, so when the menu renders, minimal processing is required.

The only downside to this approach is that the mega-menu content needs to be managed—it's not automated at all. Depending on how often the mega-menus need to be updated, the other approaches may be more suitable.

What is always true with any MODX implementation, though, is that you have options. MODX supports any kind of front-end design pattern, now or hereinafter invented—always has, always will.

For that reason alone, it will forever be my go-to web development platform. Vive la MODX!