0

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>

3
  • it would be easier to help you if you shared your code you created so far. there are many implementations of dropdown component so thats why yours might differ from our solutions. Commented May 6, 2020 at 17:14
  • I haven't created any code so far. That's my point: where is the best place to put the code? Commented May 6, 2020 at 19:37
  • To be clear, I don't need help implementing the code to populate a dropdown. This is more an architectural question. It would be good to get the simplest solution as well as an more complex solution that is more extensible. Commented May 7, 2020 at 9:10

2 Answers 2

2

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?

Sign up to request clarification or add additional context in comments.

1 Comment

I think this is the correct answer and you should go ahead and accept your own answer at this point and get your badge and your rep.
0

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>
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.