ASP.NET Core MVC / Razor Pages UI: Navigation Menu

Every application has a main menu to allow users to navigate to pages/screens of the application. Some applications may contain more than one menu in different sections of the UI.

ABP is a modular application development framework. Every module may need to add items to the menu.

So, ABP provides a menu infrastructure where;

  • The application or the modules can add items to a menu, without knowing how the menu is rendered.
  • The theme properly renders the menu.

Adding Menu Items

In order to add menu items (or manipulate the existing items) you need to create a class implementing the IMenuContributor interface.

The application startup template already contains an implementation of the IMenuContributor. So, you can add items inside that class instead of creating a new one.

Example: Add a CRM menu item with Customers and Orders sub menu items

using System.Threading.Tasks;
using MyProject.Localization;
using Volo.Abp.UI.Navigation;

namespace MyProject.Web.Menus
{
    public class MyProjectMenuContributor : IMenuContributor
    {
        public async Task ConfigureMenuAsync(MenuConfigurationContext context)
        {
            if (context.Menu.Name == StandardMenus.Main)
            {
                await ConfigureMainMenuAsync(context);
            }
        }

        private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
        {
            var l = context.GetLocalizer<MyProjectResource>();

            context.Menu.AddItem(
                new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"])
                    .AddItem(new ApplicationMenuItem(
                        name: "MyProject.Crm.Customers", 
                        displayName: l["Menu:Customers"], 
                        url: "/crm/customers")
                    ).AddItem(new ApplicationMenuItem(
                        name: "MyProject.Crm.Orders", 
                        displayName: l["Menu:Orders"],
                        url: "/crm/orders")
                     )
            );
        }
    }
}
  • This example adds items only to the main menu (StandardMenus.Main: see the Standard Menus section below).
  • It gets a IStringLocalizer from context to localize the display names of the menu items.
  • Adds the Customers and Orders as children of the CRM menu.

Once you create a menu contributor, you need to add it to the AbpNavigationOptions in the ConfigureServices method of your module:

Configure<AbpNavigationOptions>(options =>
{
    options.MenuContributors.Add(new MyProjectMenuContributor());
});

This example uses some localization keys as display names those should be defined in the localization file:

"Menu:CRM": "CRM",
"Menu:Orders": "Orders",
"Menu:Customers": "Customers"

See the localization document to learn more about the localization.

When you run the application, you will see the menu items added to the main menu:

nav-main-menu

The menu is rendered by the current UI theme. So, the look of the main menu can be completely different based on your theme.

Here, a few notes on the menu contributors;

  • ABP calls the ConfigureMenuAsync method whenever need to render the menu.
  • Every menu item can have children. So, you can add menu items with unlimited depth (however, your UI theme may not support unlimited depth).
  • Only leaf menu items have urls normally. When you click to a parent menu, its sub menu is opened or closed, you don't navigate the url of a parent menu item.
  • If a menu item has no children and has no url defined, then it is not rendered on the UI. This simplifies to authorize the menu items: You only authorize the child items (see the next section). If none of the children are authorized, then the parent automatically disappears.

Menu Item Properties

There are more options of a menu item (the constructor of the ApplicationMenuItem class). Here, the list of all available options;

  • name (string, required): The unique name of the menu item.
  • displayName (string, required): Display name/text of the menu item. You can localize this as shown before.
  • url (string): The URL of the menu item.
  • icon (string): An icon name. Free Font Awesome icon classes are supported out of the box. Example: fa fa-book. You can use any CSS font icon class as long as you include the necessary CSS files to your application.
  • order (int): The order of the menu item. Default value is 1000. Items are sorted by the adding order unless you specify an order value.
  • customData (Dictionary<string, object>): A dictionary that allows storing custom objects that you can associate with the menu item and use it while rendering the menu item.
  • target (string): Target of the menu item. Can be null (default), "_blank", "_self", "_parent", "_top" or a frame name for web applications.
  • elementId (string): Can be used to render the element with a specific HTML id attribute.
  • cssClass (string): Additional string classes for the menu item.
  • groupName (string): Can be used to group menu items.

Authorization

As seen above, a menu contributor contributes to the menu dynamically. So, you can perform any custom logic or get menu items from any source.

One use case is the authorization. You typically want to add menu items by checking a permission.

Example: Check if the current user has a permission

if (await context.IsGrantedAsync("MyPermissionName"))
{
    //...add menu items
}

For the authorization, you can use RequirePermissions extension method as a shortcut. It is also more performant, ABP optimizes the permission check for all the items.

context.Menu.AddItem(
    new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"])
        .AddItem(new ApplicationMenuItem(
                name: "MyProject.Crm.Customers",
                displayName: l["Menu:Customers"],
                url: "/crm/customers")
            .RequirePermissions("MyProject.Crm.Customers")
        ).AddItem(new ApplicationMenuItem(
                name: "MyProject.Crm.Orders",
                displayName: l["Menu:Orders"],
                url: "/crm/orders")
            .RequirePermissions("MyProject.Crm.Orders")
        )
);

You can use context.AuthorizationService to directly access to the IAuthorizationService.

Resolving Dependencies

context.ServiceProvider can be used to resolve any service dependency.

Example: Get a service

var myService = context.ServiceProvider.GetRequiredService<IMyService>();
//...use the service

You don't need to care about releasing/disposing services. ABP handles it.

The Administration Menu

There is a special menu item in the menu menu that is added by the ABP: The Administration menu. It is typically used by the pre-built admin application modules:

nav-main-menu-administration

If you want to add menu items under the Administration menu item, you can use the context.Menu.GetAdministration() extension method:

context.Menu.GetAdministration().AddItem(...)

Manipulating the Existing Menu Items

ABP executes the menu contributors by the module dependency order. So, you can manipulate the menu items that your application or module (directly or indirectly) depends on.

Example: Set an icon for the Users menu item added by the Identity Module

var userMenu = context.Menu.FindMenuItem(IdentityMenuNames.Users);
userMenu.Icon = "fa fa-users";

context.Menu gives you ability to access to all the menu items those have been added by the previous menu contributors.

Menu Groups

You can define groups and associate menu items with a group.

Example:

using System.Threading.Tasks;
using MyProject.Localization;
using Volo.Abp.UI.Navigation;

namespace MyProject.Web.Menus
{
    public class MyProjectMenuContributor : IMenuContributor
    {
        public async Task ConfigureMenuAsync(MenuConfigurationContext context)
        {
            if (context.Menu.Name == StandardMenus.Main)
            {
                await ConfigureMainMenuAsync(context);
            }
        }

        private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
        {
            var l = context.GetLocalizer<MyProjectResource>();

            context.Menu.AddGroup(
                new ApplicationMenuGroup(
                    name: "Main",
                    displayName: l["Main"]
                )
            )
            context.Menu.AddItem(
                new ApplicationMenuItem("MyProject.Crm", l["Menu:CRM"], groupName: "Main")
                    .AddItem(new ApplicationMenuItem(
                        name: "MyProject.Crm.Customers", 
                        displayName: l["Menu:Customers"], 
                        url: "/crm/customers")
                    ).AddItem(new ApplicationMenuItem(
                        name: "MyProject.Crm.Orders", 
                        displayName: l["Menu:Orders"],
                        url: "/crm/orders")
                     )
            );      
        }
    }
}

The UI theme will decide whether to render the groups or not, and if it decides to render, the way it's rendered is up to the theme. Only the LeptonX theme implements the menu group.

Standard Menus

A menu is a named component. An application may contain more than one menus with different, unique names. There are two pre-defined standard menus:

  • Main: The main menu of the application. Contains links to the page of the application. Defined as a constant: Volo.Abp.UI.Navigation.StandardMenus.Main.
  • User: User profile menu. Defined as a constant: Volo.Abp.UI.Navigation.StandardMenus.User.

The Main menu already covered above. The User menu is available when a user has logged in:

user-menu

You can add items to the User menu by checking the context.Menu.Name as shown below:

if (context.Menu.Name == StandardMenus.User)
{
    //...add items
}

IMenuManager

IMenuManager is generally used by the UI theme to render the menu items on the UI. So, you generally don't need to directly use the IMenuManager.

Example: Getting the Main menu items

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.UI.Navigation;

namespace MyProject.Web.Pages
{
    public class IndexModel : PageModel
    {
        private readonly IMenuManager _menuManager;

        public IndexModel(IMenuManager menuManager)
        {
            _menuManager = menuManager;
        }
        
        public async Task OnGetAsync()
        {
            var mainMenu = await _menuManager.GetAsync(StandardMenus.Main);
            
            foreach (var menuItem in mainMenu.Items)
            {
                //...
            }
        }
    }
}

Contributors


Last updated: July 31, 2024 Edit this page on GitHub

Was this page helpful?

Please make a selection.

To help us improve, please share your reason for the negative feedback in the field below.

Please enter a note.

Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document
Community Talks

What’s New with .NET 9 & ABP 9?

21 Nov, 17:00
Online
Register Now
Mastering ABP Framework Book
Mastering ABP Framework

This book will help you gain a complete understanding of the framework and modern web application development techniques.

Learn More