了解です。
では、前回の Bootstrap 5 多階層対応階層ドロップダウンメニュー を、
さらに ホバーで展開する版 に改良して再生成します。
これにより、クリックではなくマウスオーバーでサブメニューが開くようになります。
UI が直感的になり、ナビゲーションがスムーズになります。
1. データベース構造(同じ)
CREATE TABLE Menus (
Id INT PRIMARY KEY IDENTITY,
Title NVARCHAR(100) NOT NULL,
Url NVARCHAR(200) NULL,
ParentId INT NULL,
FOREIGN KEY (ParentId) REFERENCES Menus(Id)
);
2. モデルと DbContext
// Models/Menu.cs
using System.Collections.Generic;
public class Menu
{
public int Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public int? ParentId { get; set; }
public Menu Parent { get; set; }
public ICollection<Menu> Children { get; set; } = new List<Menu>();
}
// Data/ApplicationDbContext.cs
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
public DbSet<Menu> Menus { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Menu>()
.HasOne(m => m.Parent)
.WithMany(m => m.Children)
.HasForeignKey(m => m.ParentId)
.OnDelete(DeleteBehavior.Restrict);
}
}
3. メニュー取得サービス(階層構造構築)
// Services/MenuService.cs
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public interface IMenuService
{
Task<List<Menu>> GetMenuHierarchyAsync();
}
public class MenuService : IMenuService
{
private readonly ApplicationDbContext _context;
public MenuService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<Menu>> GetMenuHierarchyAsync()
{
var allMenus = await _context.Menus
.AsNoTracking()
.ToListAsync();
return BuildHierarchy(allMenus, null);
}
private List<Menu> BuildHierarchy(List<Menu> allMenus, int? parentId)
{
return allMenus
.Where(m => m.ParentId == parentId)
.Select(m => new Menu
{
Id = m.Id,
Title = m.Title,
Url = m.Url,
ParentId = m.ParentId,
Children = BuildHierarchy(allMenus, m.Id)
})
.ToList();
}
}
4. ViewComponent
// ViewComponents/MenuViewComponent.cs
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
public class MenuViewComponent : ViewComponent
{
private readonly IMenuService _menuService;
public MenuViewComponent(IMenuService menuService)
{
_menuService = menuService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var menuHierarchy = await _menuService.GetMenuHierarchyAsync();
return View(menuHierarchy);
}
}
5. ViewComponent のビュー(Bootstrap 5 + ホバー展開対応)
Views/Shared/Components/Menu/Default.cshtml
@model List<Menu>
@functions {
void RenderMenuItems(List<Menu> menus, bool isSubMenu = false)
{
foreach (var menu in menus)
{
if (menu.Children.Any())
{
var liClass = isSubMenu ? "dropdown-submenu" : "nav-item dropdown";
var aClass = isSubMenu ? "dropdown-item dropdown-toggle" : "nav-link dropdown-toggle";
<li class="@liClass">
<a class="@aClass" href="@(!string.IsNullOrEmpty(menu.Url) ? menu.Url : "#")" id="menu-@menu.Id" role="button" data-bs-toggle="dropdown" aria-expanded="false">
@menu.Title
</a>
<ul class="dropdown-menu" aria-labelledby="menu-@menu.Id">
@RenderMenuItems(menu.Children.ToList(), true)
</ul>
</li>
}
else
{
if (isSubMenu)
{
<li><a class="dropdown-item" href="@menu.Url">@menu.Title</a></li>
}
else
{
<li class="nav-item"><a class="nav-link" href="@menu.Url">@menu.Title</a></li>
}
}
}
}
}
<ul class="navbar-nav flex-grow-1">
@RenderMenuItems(Model)
</ul>
<style>
/* 多階層ドロップダウンの位置調整 */
.dropdown-submenu {
position: relative;
}
.dropdown-submenu > .dropdown-menu {
top: 0;
left: 100%;
margin-top: -0.5rem;
}
/* ホバーで展開 */
.dropdown:hover > .dropdown-menu {
display: block;
}
.dropdown-submenu:hover > .dropdown-menu {
display: block;
}
</style>
6. _Layout.cshtml に組み込み
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
@await Component.InvokeAsync("Menu")
</div>
7. Program.cs でサービス登録
builder.Services.AddScoped<IMenuService, MenuService>();
改良ポイント
- ホバー展開対応:CSS で
.dropdown:hover と .dropdown-submenu:hover に display: block を適用 - 多階層対応:再帰処理で階層構造を無制限に描画
- Bootstrap 5 対応:
dropdown-submenu クラスと CSS でサブメニューを右側に展開 - URL 対応:親メニューにも URL が設定されていればリンク可能
このコードなら、
- DB に階層構造を入れるだけで
_Layout.cshtml に多階層ドロップダウンメニューが表示され- マウスオーバーでサブメニューが展開されます。
もし希望があれば、このホバー展開版を スマホではクリック展開、PCではホバー展開 に切り替えるレスポンシブ対応版にもできます。
そうすればモバイルでも使いやすくなります。
このレスポンシブ対応版も作りますか?
そうすればスマホとPCで最適な動作になります。
Powered by Froala Editor