How To Create a Mega Menu In Episerver

Creating the main navigation is one of the first big tasks on any website build. On the majority of simple websites, a menu can be structured based on the top level hierarchy of the page tree, so image a scenario like:

classic_navigation

In a classic scenario Page 1, Page 2 and Page 3 would be the primary menu items. If you wanted to hide an item you would uncheck the ‘Display page in menus’ button.

var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
var menuItems = contentRepository.GetChildren<ContentPage>(ContentReference.RootPage)
.Where(x => x.VisibleInMenu)

The problem with many modern websites is the primary menu might not follow the page hierarchy structure, or, the menu might also contain promo blocks, links or images. A quote from Wikipedia ‘Mega menus are large navigation panels that typically drop down or fly out from a global nav bar’. While they certainly aren’t appropriate for every site, mega menus can create a great navigation experience for a user, when done well’

If you want a mega menu in your Episerver website, then following the traditional primary menu approach is probably the wrong pattern. In these circumstances, trying to shoe horn a menu into your pages navigation structure causes more problems than it is worth. In today’s guide, we’re going to talk about creating a separate area in your Episerver tree just for navigation to allow us to create a lot more feature rich and more complex navigation.

The Mega Menu

In today’s tutorial we will build this menu.

mega_menu

The mega menu we will create in this tutorial will not be based on the websites page hierarchy. This can be made up of any number of items that live anywhere in the site tree. Each menu item will have an image attached to it, and any of the menu items that has child sub menu pages will be rendered with an extra menu.

In order to build the menu, we will need three page types, in a menu container. This will be the area we will store the menu under, the menu to hold the menu title, the link and the sub title and lastly the sub menu to store any sub links.

Using these page types the menu will look like this:

mega_menu_editor

Menu Container

[ContentType(
DisplayName = "Container Page",
Description = "A placeholder to help organise the EPiServer page tree",
GUID = "9997138c-4469-4dbe-8adc-87b615f30f56",
GroupName = "Standard")]
public class MenuContainer : PageData, IContainerPage
{
}

Menu Page

[ContentType(
DisplayName = "Menu Page",
GUID = "A74BDA60-305C-47AA-9795-408BD343DD04",
Description = "Menu Page",
GroupName = "Standard")]
public class MenuPage : PageData
{
[Display(
Name = "Main Menu Title",
Description = "Main Menu Title",
GroupName = SystemTabNames.Content,
Order = 100)]
public virtual string MainMenuTitle { get; set; }
[Display(
Name = "Sub Menu Title",
Description = "Sub Menu Title",
GroupName = SystemTabNames.Content,
Order = 200)]
public virtual string SubMenuTitle { get; set; }
[Display(
Name = "Menu Image",
Description = "Sub Menu Title",
GroupName = SystemTabNames.Content,
Order = 300)]
[UIHint(UIHint.Image)]
public virtual Url MenuImageUrl { get; set; }
}

Sub Menu Page

To create a mega menu items sub menus I’m going to create an additional page type called sub menu. Any sub menu pages that are children under a menu item will be rendered as an additional pop-up menu. I could have also achieved the same effect by putting a content area on the menu item and adding item that way.

In this example, I’m assuming your end menu will be more complex than my basic example, so I went the page type route, but, in terms of architecture, it makes no real different in this scenario. My advice would be for a simple link list use a content area and a block, for more complex sub menus use a page type as it will give you a bit more flexibility.

[ContentType(
DisplayName = "Sub Menu Page",
GUID = "12040C26-3CD5-4D0B-BA0D-3FB7D9FBAA8E",
Description = "Menu Page",
GroupName = "Standard")]
public class SubMenuPage : PageData
{
[Display(
Name = "Menu Uel",
GroupName = SystemTabNames.Content,
Order = 100)]
public virtual Url LinkUrl { get; set; }
}

Menu Repository

public class MenuRepository
{
private readonly IEpiserverDependencies _epiServerDependencies;
public MenuRepository(IEpiserverDependencies epiServerDependencies)
{
_epiServerDependencies = epiServerDependencies;
}
public List<NavigationItem> GetMainMenu()
{
var navigationItems = new List<NavigationItem>();
var urlHelper = ServiceLocator.Current.GetInstance<UrlResolver>();
var menuContainer = 
_epiServerDependencies.ContentRepository
.GetChildren<MenuContainer>(ContentReference.RootPage)
.FirstOrDefault();
var menuPages = _epiServerDependencies.ContentRepository
.GetChildren<MenuPage>(menuContainer.ContentLink);
foreach(var menuPage in menuPages)
{
var navigationItem = new NavigationItem();
navigationItem.Name = menuPage.MainMenuTitle;
navigationItem.SubMenuTitle = menuPage.SubMenuTitle;
navigationItem.ImageUrl = menuPage.MenuImageUrl.ToString();
var subMenuPages =
_epiServerDependencies.ContentRepository
.GetChildren<SubMenuPage>(menuPage.ContentLink);
foreach(var subMenuPage in subMenuPages)
{
var subNavigationItem =
new NavigationItem
{
Name = subMenuPage.Name,
Link = subMenuPage.LinkUrl.ToString()
};
navigationItem.SubMenuItems.Add(subNavigationItem);
}
navigationItems.Add(navigationItem);
}
return navigationItems;
}
}

The View

<ul id="sdt_menu" class="sdt_menu">
@foreach (var menuItem in Model.Layout.Menu)
{
<li>
<a href="@menuItem.Link">
<img src="@Url.ContentUrl(menuItem.ImageUrl)" alt="" />
<span class="sdt_active"></span>
<span class="sdt_wrap">
<span class="sdt_link">@menuItem.Name</span>
<span class="sdt_descr">@menuItem.SubMenuTitle</span>
</span>
</a>
@if(menuItem.SubMenuItems.Any())
{
<div class="sdt_box">
@foreach(var subMenuItem in menuItem.SubMenuItems)
{
<a href="@subMenuItem.Link">@subMenuItem.Name</a>
}
</div>
}
</li>
}
</ul>

Conclusion

In today’s tutorial, we’ve talked about the difference between a simple main navigation that is limited to the hierarchy of your page tree and a more complex menus that will not limit you and allow you to create more flexible menus like a mega menu.

To create the mega menu we create an area outside of the start page that will be used as a container for the menu. The menu will then be built up from two different page types, a menu page and a sub menu page.

The sub menu page type could be swapped out for a content area in the menu page. Using a page type allows us a bit more flexibility but the difference isn’t worth it.

To create the menu we built a menu repository that built the menu and returned it in the master layout file (download the code sample for this).

Code Sample

As always a fully working code sample can be downloaded from my Github page EpiserverMegaMenu.

Jon D Jones

Software Architect, Programmer and Technologist Jon Jones is founder and CEO of London-based tech firm Digital Prompt. He has been working in the field for nearly a decade, specializing in new technologies and technical solution research in the web business. A passionate blogger by heart , speaker & consultant from England.. always on the hunt for the next challenge

More Posts

2 replies
  1. Andrew
    Andrew says:

    Thanks for the tutorial! This is a great idea. I’ve got a question, though: if you create a new tree structure for a new menu, how do you make sure that the pages have the right breadcrumb trails etc? I’d welcome any idea you have to deal with that.

    Reply
    • Jon D Jones
      Jon D Jones says:

      You are correct, depending on your requirements your breadcrumbs may not exactly match the menu structure. If you wanted your breadcrumbs to match you could create a custom breadcrumb plug-in. The plug-in would look for the current page in the menu tree, and then created the breadcrumb from there. If it couldn’t find the page then it could default to a normal breadcrumb, this would give you the best of both worlds 🙂

      Reply

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *