I have created a very simple ASP.NET Core 3.1 WebApp that uses Razor pages rather than MVC. I want to add a dynamic dropdown list to the top menu bar which is shared by all other pages. I've added the necessary HTML to _Layout.cshtml so I get a nice dropdown box along with my other menu content (Home, About, etc) - see below.
What are my options for adding the code that populates the dropdown in my _Layout.cshtml file?
<!DOCTYPE html>
<html lang="en">
<head>
...
</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"><img src="images/Logo.png" alt="logo" height="50"></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
<li class="nav-item dropdown ml-auto">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownTZ" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Whisky
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">Gin</a>
<a class="dropdown-item" href="#">Vodka</a>
</div>
</li>
</ul>
</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">
XXX © 2020 - v1.1.30.1 - your use of this website is subject to our <a asp-area="" asp-page="/TermsConditions">Terms & Conditions</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>
@RenderSection("Scripts", required: false)
</body>
</html>
A reasonable solution is to create a View Component and then add it to _Layout.cshtml. I found a good step-by-step guide by Peter Kellner (Progress) which helped me on my way. Essentially my solution involved:
In terms of code:
TimeZoneControlViewComponent.cs - ViewComponents support dependency injection so it is possible to put code into the constructor to support database access as described in Microsoft Docs
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace C19QuarantineWebApp.Pages.Components.TimeZoneControl
{
public class TimeZoneControlViewComponent : ViewComponent
{
public TimeZoneControlViewComponent() { }
public IViewComponentResult Invoke(string timeZoneId)
{
var timeZones = new List<string>();
timeZones.AddRange(new[] {"GMT", "CET", "IST"});
return View("Default", timeZones.ToArray());
}
}
}
Default.cshtml
@model string[]
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownTZ" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Time zones
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
@foreach(var tz in Model) {<a class="dropdown-item" href="#">@tz</a>}
</div>
_Layout.cshtml - replace the original dropdown (see question) with
<vc:time-zone-control time-zone-id="xxx">
</vc:time-zone-control>
In context this gives:
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<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="/About">About</a>
</li>
<li class="nav-item dropdown ml-auto">
<vc:time-zone-control time-zone-id="xxx">
</vc:time-zone-control>
</li>
</ul>
</div>
I would be interested to receive comments on this solution. Is there an easier and more elegant way to do this?
I would say for a better Razor solution not using view components (MVC) I would create an class and use @inject on your menu page. Fewer steps. See below.
Build your MenuService Class. Here is the example:
public class MenuService
{
private readonly CoreContext _dbContext;
public MenuService(CoreContext dbContext)
{
_dbContext = dbContext;
}
public IEnumerable<tblMenu> GetMenuMaster()
{
return _dbContext.tblMenu.AsEnumerable();
}
public IEnumerable<tblMenu> GetMenuMaster(string UserRole, bool isAdmin)
{
if (isAdmin == false)
{
var result = _dbContext.tblMenu.Where(m => m.UserRole != "Admin" ).ToList();
return result;
}
else
{
var result = _dbContext.tblMenu.Where(m => m.Something == `UserRole).ToList();`
return result;
}
}
}
Then finally, AddTransient to startup.cs:
services.AddTransient<MenuService, MenuService>();
Now in the _Layout.cshtml you can add the menu:
@using System.Security.Claims;
@inject Solution1.Services.MenuService menus
<!DOCTYPE html>
<html>
...rest of your _Layout.cshtml page here
Now within your HTML for the menu...customize as needed.
@foreach (var subMenu in menus.GetMenuMaster(role1.Value, admin))
{
<a class="dropdown-item" asp-page="./pathtopage" asp-page-handler="List" asp-route id="@subMenu.Program"><i class="@subMenu.Class" title="history"></i>@subMenu.DisplayName
</a>
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.