skip to Main Content

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.

2

Answers


  1. The recommendation for data-driven components that depend on backend logic and are intended for the layout page is a View Component.

    The code from Main.cshtml will go into the component’s default.cshtml file, and the backend logic goes into the ViewComponent class file, which supports dependency injection.

    Login or Signup to reply.
  2. 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="[email protected]_ID" role="button" data-bs-toggle="dropdown" aria-expanded="false">@menuItem.Form_Title</a>
                <ul class="dropdown-menu" aria-labelledby="[email protected]_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">
                &copy; 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>
    

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search