I try create dynamic menu but i have problem with adding parameters in link to the controller and action, can i add parameter to url.content? or there are the other way please give your insight it will very helpfull
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Menu Example</a>
</div>
<ul class="nav navbar-nav">
#if (Session["Menu"] != null)
{
var MenuMaster = (List<Accountstatement.Models.MenuModel>)Session["Menu"];
var groupByMenu = MenuMaster.GroupBy(x => x.MainMenuName).ToList();
foreach (var MenuList in groupByMenu)
{
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">#MenuList.Key<span class="caret"></span></a>
<ul class="dropdown-menu">
#foreach (var SubMenuList in MenuList)
{
<li>#SubMenuList.SubMenuName</li>
}
</ul>
</li>
}
}
</ul>
this is the part the url.content is, how can i put the parameter inside url.content?
<li>#SubMenuList.SubMenuName</li>
Use #Html.Raw with Url.Content and pass parameters like below:
#Html.Raw(Url.Content("~/"+#SubMenuList.ControllerName +"/"+ #SubMenuList.ActionName?param=" + param.value))
Related
I am trying to make a simple dropdown menu in my Blazor app. It is declared in my Header.razor as shown below:
<div class="header-sign">
<div class="dropdown" id="toggleDropdown">
<button class="button-md" type="button">
<i data-feather="user"></i>
<span>Personal cab</span>
</button>
<ul class="dropdown-list">
<li class="dropdown-item">
<a class="dropdown-link" href="">
<p class="text-md">Cabinet</p>
</a>
</li>
<li class="dropdown-item">
<a class="dropdown-link" href="">
<p class="text-md">Sign out</p>
</a>
</li>
</ul>
</div>
</div>
The dropdown can be triggered via JS script like this:
$("#toggleDropdown").on("mouseenter", () => {
$(this).find(".dropdown-list").addClass("is-active");
});
I have my header declared in MainLayout.razor like shown below:
#inherits LayoutComponentBase
<Header></Header>
<main class="main">
#Body
</main>
<Footer></Footer>
And this approach doesn't work.
Everything changes if I replace the above-mentioned Header.razor with razor page Header.cshtml with exactly the same markup and render it in _Layout.cshtml in such a way:
#await Html.PartialAsync("Header")
Looks like the above markup can only work in *.cshtml files but cannot in *.razor and I don't quiet understand the reason. Why does it behave this way?
Huge thanks to Mohammed Alwedaei, I figured out how to solve my question. His remark that the dropdown should be activated on a click and not on a hover really solved it to me.
We should just keep state of a dropdown list and toggle classes of it in a bit tricky way (with a ternary operator #(isActive ? "is-active" : "")). To make the dropdown work as intended, we should define #onclick attribute and also #onblur to disable it when user clicks somewhere else.
The code below works just fine:
<div class="header-sign">
<div class="dropdown" id="toggleDropdown">
<button #onclick="Show" #onblur="Hide" class="button-md" type="button">
<i data-feather="user"></i>
<span>Personal cab</span>
</button>
<ul class="dropdown-list #(isActive ? "is-active" : "")">
<li class="dropdown-item">
<a class="dropdown-link" href="/cabinet">
<p class="text-md">Cabinet</p>
</a>
</li>
<li class="dropdown-item">
<a #onclick="LogOutAsync" class="dropdown-link" href="">
<p class="text-md">LogOut</p>
</a>
</li>
</ul>
</div>
</div>
#code {
private bool isActive = false;
private void Show()
{
isActive = true;
}
private void Hide()
{
isActive = false;
}
private async Task LogOutAsync()
{
}
}
I have created a multilevel dropdown which is dynamic. When I am clicking the third level item(new, in progress, resolved which are from the database)it should be selected and only when I am clicking the create button that selected item data should be passed to the controller.
multilevel dropdown code
<div class="dropdown drop">
<button class="btn dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-mdb-toggle="dropdown"
aria-expanded="false">
STATUS
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<li>
<a class="dropdown-item" href="#">
ProjectStatus »
</a>
<ul class="dropdown-menu dropdown-submenu">
#foreach (var i in ViewBag.ProjectStatus)
{
<li>
#*<a id="ProjectStatus" class="dropdown-item" href="#">#i.Text</a>*#
<select asp-for="ProjectStatus" class="form-control" value="#i.Text">#i.Text</select>
</li>
}
</ul>
</li>
<li>
<a class="dropdown-item" href="#">
DevelopmentStatus »
</a>
<ul class="dropdown-menu dropdown-submenu">
#foreach (var i in ViewBag.DevelopmentStatus)
{
<li>
<a class="dropdown-item" href="#">#i.Text</a>
</li>
}
</ul>
</li>
</ul>
</div>
HttpGet create method
public IActionResult Create()
{
ViewBag.ProjectStatus = new SelectList(_context.ProjectStatus, "ProjectStatusID", "Status");
ViewBag.DevelopmentStatus = new SelectList(_context.DevelopmentStatus, "DevelopmentStatusID", "Status");
return View();
}
Need to get the selected list item data to the post method
[HttpPost]
public async Task<IActionResult> Create(Projects ec) {}
You can try to put the selected item value to a hidden input,and then change the background of the <li></li>.Here is a demo:
<div class="dropdown drop">
<button class="btn dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-mdb-toggle="dropdown"
aria-expanded="false">
STATUS
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<li>
<a class="dropdown-item" href="#">
ProjectStatus »
</a>
<ul class="dropdown-menu dropdown-submenu">
#foreach (var i in ViewBag.ProjectStatus)
{
<li>
<a class="dropdown-item" href='#' onclick='return change("#i.Text",this,"ProjectStatus")'>#i.Text</a>
</li>
}
</ul>
</li>
<li>
<a class="dropdown-item" href="#">
DevelopmentStatus »
</a>
<ul class="dropdown-menu dropdown-submenu">
#foreach (var i in ViewBag.DevelopmentStatus)
{
<li>
<a class="dropdown-item" href='#' onclick='return change("#i.Text",this,"DevelopmentStatus")'>#i.Text</a>
</li>
}
</ul>
</li>
</ul>
</div>
<input id="ProjectStatus" name="ProjectStatus" hidden />
<input id="DevelopmentStatus" name="DevelopmentStatus" hidden />
<script>
function change(text, t, type) {
$("li").css("background", "transparent");
if (type == "ProjectStatus") {
$("#ProjectStatus").val(text);
$("#DevelopmentStatus").val("");
} else {
$("#DevelopmentStatus").val(text);
$("#ProjectStatus").val("");
}
$(t).parent().css("background", "#1266f1");
return false;
}
</script>
When select ProjectStatus,the selected value will be put to $("#ProjectStatus"),and the value of $("#DevelopmentStatus") will be "".When select DevelopmentStatus,the selected value will be put to $("#DevelopmentStatus"),and the value of $("#ProjectStatus") will be "".
result:
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:
Creating a folder under Pages called 'Components'
Creating a subfolder under 'Components' called 'TimeZoneControl'
Adding a class called 'TimeZoneControlViewComponent.cs' in 'TimeZoneControl'
Adding a Razor View called 'Default.cshtm' in 'TimeZoneControl'
Modifying my _Layout.cshtml to include my TimeZoneControl View Component
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>
}
As far as I understand the Partial pages have only the cshtml code, while the ViewComponent have only the cs code.
Perhaps my approach is wrong, but I'm trying to render a complete page (cshtml + cshtml.cs) in another.
Real example.
Visual Studio 2017, ASP.NET Core 2.
NavBar.cshtml:
#page
#model MyProject.NavBarModel
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" asp-page="/Machines">#Resources.Machines</a></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">#Resources.Orders</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" asp-page="/RunningOrders">#Resources.Running <span class="badge badge-primary">#ViewData["running"]</span></a>
<a class="dropdown-item" asp-page="/OpenOrders">#Resources.Opened <span class="badge badge-primary">#ViewData["running"]</span></a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" asp-page="/SuspendedOrders">#Resources.Suspended <span class="badge badge-danger">#ViewData["running"]</span></a>
</div>
</li>
<li class="nav-item"><a class="nav-link" asp-page="/Tables">#Resources.Lists</a></li>
<li class="nav-item"><a class="nav-link" asp-page="/About">#Resources.About</a></li>
</ul>
</div>
NavBar.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using MyProject.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyProject
{
public class NavBarModel : PageModel
{
private readonly MyContext _context;
public NavBarModel(MyContext context)
{
_context = context;
}
public List<Order> Orders { get; set; }
public async Task OnGetAsync()
{
Orders = await _context.Orders.ToListAsync();
ViewData["open"] = _context.Orders.Where(x => x.State == OrderStates.Open).Count();
ViewData["running"] = _context.Orders.Where(x => x.State == OrderStates.Running).Count();
ViewData["suspened"] = _context.Orders.Where(x => x.State == OrderStates.Suspended).Count();
}
}
}
_Layout.cshtml:
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top mb-2">
<a asp-page="/Index" class="navbar-brand">Production Assistant</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
#Html.Partial("NavBar", new NavBarModel())
</nav>
<div class="container body-content">
#RenderBody()
</div>
</body>
In this example I'm trying to add a Badge in the Navbar. Because the _Layout file has no code behind I had to put that in a separate Page (NavBar).
The problem is that when I try to render the page it asks for the constructor parameters. I'm afraid I've broken the dependency injection in this way.
In View have the following foreach loop to display links:
#model List<IGrouping<string, FileInfo>>
#{
ViewData["Title"] = "Index";
int i = 0;
}
<div class="container-fluid main-container">
<div class="col-md-3 sidebar">
<ul class="nav nav-pills nav-stacked">
#foreach (var link in #Model)
{
<li>#link.Key <span>(***)</span></li>
foreach(var item in Model[i])
{
#item.Culture;
}
i++;
}
</ul>
</div>
The nested foreach displays the correct info but on a different line - I would like it positioned where the *** is in code listing.
Also I would like a comma ',' placed between each culture.
Any help appreciated.
Current output showing as:
EDIT: updated code to following and still getting same issue
#foreach (var link in #Model)
{
<li>
#link.Key
<span>
#foreach(var item in link)
{
#item.Culture
}
</span>
</li>
}
What about something like this? (untested)
<div class="container-fluid main-container">
<div class="col-md-3 sidebar">
<ul class="nav nav-pills nav-stacked">
#foreach (var link in #Model)
{
<li>#link.Key <span>
#String.Join(", ", link.Select(item => item.Culture).ToArray())
</span></li>
}
</ul>
</div>
Do you mean like this ?
<div class="container-fluid main-container">
<div class="col-md-3 sidebar">
<ul class="nav nav-pills nav-stacked">
#foreach (var link in #Model)
{
<li>#link.Key <span>
foreach(var item in Model[i])
{
#item.Culture;
#:,
}
</span></li>
i++;
}
</ul>
With the power of razor something like this should work.
Edit: just realised you want all cultures so:
#foreach (var link in #Model)
{
var Culture = "";
#foreach( var item in Model[i])
{
Culture += string.format("{0}," #item.culture;)
}
<li>#link.Key <span>(#Culture.trimend(','))</span></li>
i++;
}