I am developing an ASP.NET Core 8.0 application using Razor Pages. I have implemented a dynamic navigation menu that needs to be consistently displayed across all pages of my application. The navigation menu items are fetched dynamically based on the user's role from a database.
I have successfully implemented a navigation menu using Razor Pages and I'm currently displaying it on a dedicated Main.cshtml page. However, when a user navigates to another page, the new page opens independently of the navigation menu, which I want to remain consistent across all pages.
Main.cshtml code:
<ul class="horizontal-list ">
@foreach (var menuItem in Model.MenuItems.Where(m => m.parentId == null))
{
<li>
<a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">@menuItem.formName</a>
<ul class="dropdown-menu">
@foreach (var subMenuItem in Model.MenuItems.Where(m => m.parentId == menuItem.formID))
{
@if (Model.MenuItems.Any(m => m.parentId == subMenuItem.formID))
{
<li class="dropdown-submenu">
<a class="dropdown-item dropdown-toggle menutext" href="#">@subMenuItem.formName</a>
<ul class="dropdown-menu">
@foreach (var subsubMenuItem in Model.MenuItems.Where(m => m.parentId == subMenuItem.formID))
{
<li class="dropdown-menu-1">
<a class="dropdown-item menutext" href="@subsubMenuItem.formUrl">@subsubMenuItem.formName</a>
</li>
}
</ul>
</li>
}
else
{
<li class="dropdown-submenu">
<a class="dropdown-item menutext" href="@subMenuItem.formUrl">@subMenuItem.formName</a>
</li>
}
}
</ul>
</li>
}
</ul>
Main.cshtml.cs code:
public List<Menu> GetMenusForRole(long roleId)
{
var menuList = new List<Menu>();
// Example logic to fetch menus based on roleId from a database
ArrayList parameters = new ArrayList { roleId };
DataTable dtMenus = ExecuteStoredProcedure.ExecProc_getDataTable(_configuration, "Stored_Procedure_Name", parameters);
foreach (DataRow row in dtMenus.Rows)
{
menuList.Add(new Menu
{
formID = Convert.ToInt64(row["Form_ID"].ToString()),
formName = row["Form_Title"].ToString(),
parentId = row["Form_Parent_ID"] == DBNull.Value ? (long?)null : Convert.ToInt64(row["Form_Parent_ID"].ToString()),
formUrl = row["Form_Url"].ToString()
});
}
return menuList;
}
What I've Tried Initially, I created a Main.cshtml and Main.cshtml.cs to display the navigation dropdowns, but this approach leads to the new pages opening independently. I attempted to move the navigation bar into the _Layout.cshtml file, which is used as the main layout template across all pages. However, since the menu items are dynamically fetched based on user roles, I encountered difficulties because _Layout.cshtml does not directly support backend logic.
Desired Outcome: I want to have a navigation menu that remains fixed across all pages of my application, where each menu item is dynamically fetched based on the user's role. When a user navigates to a new page, I want this page to be displayed within the context of the navigation menu, without the menu itself being refreshed.
you could try to place the navigation menu in the _Layout.cshtml
file as it does not support backend logic, you will need to make use of a combination of dependency injection and a view component.
NavigationMenuViewComponent.cs:
using DynamicMenuProject.Data;
using DynamicMenuProject.Models;
using Microsoft.AspNetCore.Mvc;
namespace DynamicMenuProject.Pages.Components
{
public class NavigationMenuViewComponent : ViewComponent
{
private readonly ApplicationDbContext _context;
public NavigationMenuViewComponent(ApplicationDbContext context)
{
_context = context;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var roleId = GetUserRoleId(); // Implement this method to get the current user's role ID
var menuItems = await Task.Run(() => GetMenusForRole(roleId));
return View(menuItems);
}
private List<Menu> GetMenusForRole(long roleId)
{
return _context.Menus
.Where(m => m.Role_Id == roleId)
.Select(m => new Menu
{
Form_ID = m.Form_ID,
Form_Title = m.Form_Title,
Form_Parent_ID = m.Form_Parent_ID,
Form_Url = m.Form_Url ?? string.Empty, // Provide a default value for NULL URLs
Role_Id = m.Role_Id
})
.ToList();
}
private long GetUserRoleId()
{
// Implement logic to retrieve the current user's role ID
return 1; // Example role ID
}
}
}
Views/Shared/Components/NavigationMenu/Default.cshtml:
@model List<DynamicMenuProject.Models.Menu>
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
@foreach (var menuItem in Model.Where(m => m.Form_Parent_ID == null))
{
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown_@menuItem.Form_ID" role="button" data-bs-toggle="dropdown" aria-expanded="false">@menuItem.Form_Title</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown_@menuItem.Form_ID">
@foreach (var subMenuItem in Model.Where(m => m.Form_Parent_ID == menuItem.Form_ID))
{
@if (Model.Any(m => m.Form_Parent_ID == subMenuItem.Form_ID))
{
<li class="dropdown-submenu">
<a class="dropdown-item dropdown-toggle" href="#">@subMenuItem.Form_Title</a>
<ul class="dropdown-menu">
@foreach (var subsubMenuItem in Model.Where(m => m.Form_Parent_ID == subMenuItem.Form_ID))
{
<li>
<a class="dropdown-item" href="@subsubMenuItem.Form_Url">@subsubMenuItem.Form_Title</a>
</li>
}
</ul>
</li>
}
else
{
<li>
<a class="dropdown-item" href="@subMenuItem.Form_Url">@subMenuItem.Form_Title</a>
</li>
}
}
</ul>
</li>
}
</ul>
_Layout.cshtml:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - DynamicMenuProject</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/DynamicMenuProject.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">DynamicMenuProject</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
</li>
</ul>
<!-- Render the dynamic navigation menu -->
@await Component.InvokeAsync("NavigationMenu")
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2024 - DynamicMenuProject - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>