完成了增刪改查以及頁面展示,這一節我們來為任務清單添加【導航菜單】。
在以往的項目中,大家可能會手動在layout頁面中添加一個a標簽來新增導航菜單,這也是一種方式,但是如果要針對不同用戶不同權限決定是否顯示某個菜單,那么直接在layout頁面中去控制就不方便了。
不過,ABP已經為大家考慮了這一點,集成了通用的創建和顯示菜單的方式。其主要代碼集成在Abp.Application.Navigation
命名空間下,相應源碼在此。
下面我們就來梳理下導航菜單是如何實現和使用。
一、如何使用Abp集成的導航菜單
針對我們的『任務清單』Deom,我們需要在導航欄上添加一個【Task List】的菜單入口。
1.打開web展現層,定位到App_Start/xxxNavigationProvider.cs。
public class LearningMpaAbpNavigationProvider : NavigationProvider
{
public override void SetNavigation(INavigationProviderContext context)
{
context.Manager.MainMenu
.AddItem(
new MenuItemDefinition(
"Home",
L("HomePage"),
url: "",
icon: "fa fa-home",
requiresAuthentication: true
)
).AddItem(
new MenuItemDefinition(
"Tenants",
L("Tenants"),
url: "Tenants",
icon: "fa fa-globe",
requiredPermissionName: PermissionNames.Pages_Tenants
)
).AddItem(
new MenuItemDefinition(
"Users",
L("Users"),
url: "Users",
icon: "fa fa-users",
requiredPermissionName: PermissionNames.Pages_Users
)
).AddItem(
new MenuItemDefinition(
"About",
L("About"),
url: "About",
icon: "fa fa-info"
)
);
}
}
該文件中默認定義了Home、Tenants、Users、About四個菜單。注意觀察的話,想必會注意到Home菜單設置了requiresAuthentication: true
,即只有登陸后才會顯示該菜單。Tenants和Users菜單設置了requiredPermissionName
屬性,即用戶具有指定的權限時才顯示菜單。About菜單沒有限制,默認顯示。
每一個菜單項都是一個MenuItemDefinition
,其中主要包括Name
(唯一名稱),DisplayName
(本地化顯示名稱),Url
(菜單跳轉),Icon
(指定菜單圖標)。
解釋到這里,大家自己都可以依葫蘆畫瓢,新增菜單了。
2.添加[Task List]菜單項
AddItem(
new MenuItemDefinition(
"TaskList",
L("Task List"),
url: "Tasks/Index",
icon: "fa fa-tasks",
requiresAuthentication: true
)
保存,刷新頁面即可看到新增的[Task List]菜單了。
PS:之所以頁面上會顯示為[Task List],是因為我們並沒有維護本地化資源文件。在對應的本地化配置文件中新增名為"Task List"的即可。比如在中文的本地化文件中添加
<text name="Task List" value="任務清單" />
,重新啟站點,將語言切換為中文。
二、Abp集成的導航菜單的代碼結構
1.先來看看相關代碼的類型依賴關系圖
分析發現,abp集成的導航菜單實際上是應用了組合設計模式。
其中MenuDefinition為根節點,MenuItemDefinition為樹枝節點,其中MenuItemDefinition中也維護了一個List
2.從圖中可以看出,主要是由以下幾部分組成:
- MenuDefinition/MenuItemDefinition:菜單/菜單項定義。其中菜單項包括
Name
(唯一名稱),DisplayName
(本地化顯示名稱),Url
(菜單跳轉),Icon
(指定菜單圖標)。此外,可以通過指定RequiresAuthentication=true
來限制菜單項只有對登錄用戶可見,同時也可以指定RequiredPermissionName
來限定當用戶有某個權限時菜單才可見。 - UserMenu/UserMenuItem:封裝了用於顯示給用戶的菜單/子菜單集合。
- INavigationConfiguration/NavigationConfiguration:導航配置,維護了
NavigationProvider
的列表。 - NavigationProvider:Provider模式(將api的定義和實現分離)。抽象類,定義了
SetNavigation
方法,在需要設置導航的項目中實現該類,並在對應的模塊中PreInitialize
方法中注冊***NavigationProvider
的實現。 - INavigationManager/NavigationManager:其中接口中定義了一個Dictionary,用來存儲應用程序中定義的所有菜單項,和一個MainMenu。注入了對
INavigationConfiguration
的引用,以便在NavigationManager
中的Initialize
方法遍歷INavigationConfiguration
配置的NavigationProvider
列表進行菜單的初始化。
IUserNavigationManager/UserNavigationManager:是對NavigationManager的一次封裝。其中接口中定義了GetMenuAsync
的重載方法,用來獲取指定用戶的菜單。 - INavigationProviderContext/NavigationProviderContext:封裝了INavigationManager的上下文類,用於傳參。
三、Abp集成的導航菜單的具體實現
核心邏輯:NavigationManager遍歷NavigationConfiguration中維護的NavigationProvider列表,並調用NavigationProvider實現的SetNavigation方法來完成導航菜單的初始化。
NavigationManager負責初始化菜單
NavigationConfiguration負責維護NavigationProvider的實現列表。
具體的NavigationProvider的實現
在對應的模塊中注冊具體的NavigtionProvider到INavigationConfiguration維護的列表中。
UserNavigationManager對NavigationManager進行了進一步的封裝,根據用戶和權限去創建和獲取菜單。
在LayoutController中,通過注入對IUserNavigationManager的引用,來獲取菜單,並由_TopMenu分部頁進行最終呈現。
public class LayoutController : LearningMpaAbpControllerBase
{
private readonly IUserNavigationManager _userNavigationManager;
private readonly ISessionAppService _sessionAppService;
private readonly IMultiTenancyConfig _multiTenancyConfig;
private readonly ILanguageManager _languageManager;
public LayoutController(
IUserNavigationManager userNavigationManager,
ISessionAppService sessionAppService,
IMultiTenancyConfig multiTenancyConfig,
ILanguageManager languageManager)
{
_userNavigationManager = userNavigationManager;
_sessionAppService = sessionAppService;
_multiTenancyConfig = multiTenancyConfig;
_languageManager = languageManager;
}
[ChildActionOnly]
public PartialViewResult TopMenu(string activeMenu = "")
{
var model = new TopMenuViewModel
{
MainMenu = AsyncHelper.RunSync(() => _userNavigationManager.GetMenuAsync("MainMenu", AbpSession.ToUserIdentifier())),
ActiveMenuItemName = activeMenu
};
return PartialView("_TopMenu", model);
}
}
至此,我們完成了對導航菜單的梳理和總結。