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
fromcontext
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:
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
url
s normally. When you click to a parent menu, its sub menu is opened or closed, you don't navigate theurl
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 is1000
. 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 benull
(default), "_blank", "_self", "_parent", "_top" or a frame name for web applications.elementId
(string
): Can be used to render the element with a specific HTMLid
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 theIAuthorizationService
.
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:
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:
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)
{
//...
}
}
}
}