Dynamic Menus in ASP.NET Core

By FoxLearn 2/28/2025 4:44:49 AM   110
Implementing dynamic menus on the layout page in ASP.NET Core involves several steps.

1. Enable Sessions in ASP.NET Core

Sessions are used to store user-specific data, such as menu items, across requests. To enable sessions, configure them in the Program.cs file.

var builder = WebApplication.CreateBuilder(args);

// Add session services
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30); // Session timeout
    options.Cookie.HttpOnly = true; // Prevent client-side script access
    options.Cookie.IsEssential = true; // Mark the session cookie as essential
});

var app = builder.Build();

// Add session middleware to the pipeline
app.UseSession();

app.Run();

2. Define the Menu Model

Create a model to represent the menu structure. For example, you can define a Menu class with properties like IdNameAction, and SubMenus.

public class Menu
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Action { get; set; }
    public string Controller { get; set; }
    public List<Menu> SubMenus { get; set; } = new List<Menu>();
}

3. Store Menu Data in Session

After a user logs in or during application startup, fetch the menu data (e.g., from a database or configuration) and store it in the session.

public class AccountController : Controller
{
    [HttpPost]
    public async Task<IActionResult> Login(LoginModel model)
    {
        if (ModelState.IsValid)
        {
            // Authenticate the user
            var user = await _userService.AuthenticateAsync(model.Username, model.Password);

            if (user != null)
            {
                // Fetch menu data based on user role
                var menus = await _menuService.GetMenusByRoleAsync(user.Role);

                // Store menu data in session
                HttpContext.Session.SetString("Menus", JsonConvert.SerializeObject(menus));

                return RedirectToAction("Dashboard", "Home");
            }
            else
            {
                ModelState.AddModelError("", "Invalid username or password.");
            }
        }

        return View(model);
    }
}

4. Retrieve and Display Menu in _Layout.cshtml

In the layout page (_Layout.cshtml), retrieve the menu data from the session and render it dynamically.

Inject IHttpContextAccessor: Inject IHttpContextAccessor to access the session in the view.

@inject IHttpContextAccessor HttpContextAccessor
@using Newtonsoft.Json

Retrieve Menu Data: Deserialize the menu data from the session.

@{
    var menusJson = HttpContextAccessor.HttpContext?.Session.GetString("Menus");
    List<Menu> menus = new List<Menu>();

    if (!string.IsNullOrEmpty(menusJson))
    {
        menus = JsonConvert.DeserializeObject<List<Menu>>(menusJson);
    }
}

Render the Menu: Use a loop to render the menu dynamically.

<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container-fluid">
        <a class="navbar-brand" href="/">My App</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">
                @if (menus != null)
                {
                    @foreach (var menu in menus)
                    {
                        <li class="nav-item">
                            <a class="nav-link" href="@Url.Action(menu.Action, menu.Controller)">@menu.Name</a>
                            @if (menu.SubMenus != null && menu.SubMenus.Any())
                            {
                                <ul class="dropdown-menu">
                                    @foreach (var subMenu in menu.SubMenus)
                                    {
                                        <li><a class="dropdown-item" href="@Url.Action(subMenu.Action, subMenu.Controller)">@subMenu.Name</a></li>
                                    }
                                </ul>
                            }
                        </li>
                    }
                }
            </ul>
        </div>
    </div>
</nav>

Use View Components for Reusability

For better organization, you can encapsulate the menu rendering logic in a View Component.

Create a View Component: Create a MenuViewComponent class.

public class MenuViewComponent : ViewComponent
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public MenuViewComponent(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        var menusJson = _httpContextAccessor.HttpContext?.Session.GetString("Menus");
        List<Menu> menus = new List<Menu>();

        if (!string.IsNullOrEmpty(menusJson))
        {
            menus = JsonConvert.DeserializeObject<List<Menu>>(menusJson);
        }

        return View(menus);
    }
}

Create the View Component View: Create a view for the component at Views/Shared/Components/Menu/Default.cshtml.

@model List<Menu>

<ul class="navbar-nav">
    @foreach (var menu in Model)
    {
        <li class="nav-item">
            <a class="nav-link" href="@Url.Action(menu.Action, menu.Controller)">@menu.Name</a>
            @if (menu.SubMenus != null && menu.SubMenus.Any())
            {
                <ul class="dropdown-menu">
                    @foreach (var subMenu in menu.SubMenus)
                    {
                        <li><a class="dropdown-item" href="@Url.Action(subMenu.Action, subMenu.Controller)">@subMenu.Name</a></li>
                    }
                </ul>
            }
        </li>
    }
</ul>

Render the View Component in _Layout.cshtml: Invoke the view component in the layout page.

<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container-fluid">
        <a class="navbar-brand" href="/">My App</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">
            @await Component.InvokeAsync("Menu")
        </div>
    </div>
</nav>

By following these steps, you can implement dynamic menus in ASP.NET Core that adapt to user roles and permissions. Using sessions and view components ensures a clean, maintainable, and scalable solution.