简体   繁体   中英

ASP.NET Core Simple Razor Page WebApp - adding a dynamic dropdown list to the top menu bar

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 &copy; 2020&nbsp;-&nbsp;v1.1.30.1&nbsp;-&nbsp;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:

  1. Creating a folder under Pages called 'Components'
  2. Creating a subfolder under 'Components' called 'TimeZoneControl'
  3. Adding a class called 'TimeZoneControlViewComponent.cs' in 'TimeZoneControl'
  4. Adding a Razor View called 'Default.cshtm' in 'TimeZoneControl'
  5. Modifying my _Layout.cshtml to include my TimeZoneControl View Component
  6. Adding @addTagHelper *, MyWebApp to the end of _ViewImports.cshtml where MyWebApp is the name of my Visual Studio webapp project.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM