How to make a dropdown menu in Blazor - c#

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()
{
}
}

Related

How to add parameters in url.content

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

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

Sharing a single property across multiple cshtml pages in Blazor

I'm struggling to share a properties value across components in Blazor. Basically I have a button with an onclick event this sets a value on a function depending on the value another cshtml page should react to the value and it doesn't. The issue is that because both cshtml pages inherit the same function they have their own instance of the function. Here's my code so far:
the Function:
public class MenuFunctions : BlazorComponent, IMenuFunctions
{
public bool CollapseNavMenu
{
get ; set;
}
public void ToggleNavMenu()
{
CollapseNavMenu = !CollapseNavMenu;
}
}
The main button on NavMenuToggleComponent.cshtml:
#inherits MenuFunctions
<div class="pl-4 navbar navbar-dark">
<button class="navbar-toggler navbar-brand main-button" title="MENU" onclick=#ToggleNavMenu>
MENU
</button>
</div>
my NavMenu.cshtml file:
#inherits MenuFunctions
<div class="#(CollapseNavMenu ? "collapse" : null)" onclick=#ToggleNavMenu>
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="home">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="frontpage">
<span class="oi oi-home" aria-hidden="true"></span> Front Page
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match=NavLinkMatch.All>
<span class="oi oi-account-login" aria-hidden="true"></span> Login
</NavLink>
</li>
</ul>
</div>
putting it all together in my MainLayout.cshtml
#inherits BlazorLayoutComponent
<NavMenuToggleComponent></NavMenuToggleComponent>
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
About
<NavLink class="nav-link pull-right" href="logout">
<span class="oi oi-account-logout" aria-hidden="true"></span> Logout
</NavLink>
</div>
<div class="content px-4">
#Body
</div>
</div>
so when I click the button in NavMenuToggleComponent.cshtml I want the CollapseNavMenu to react in the NavMenu.cshtml file
If I remove the use of the interface, then inject the components onto the pages as opposed to Inherit and then add the following into the startup:
services.AddSingleton<MenuFunctions>();
the pages will load, but I still get the same issue.
The solution here would be to bring the shared property to the place where it is common, in this case the MainLayout.cshtml and not make the components dependent on a common interface.
So the MainLayout.cshtml would hold the state in a new property and that property is bound to the two components.
You should not use DI for component classes, only for service providing classes and alike.

Selenium webdriver C# hyperlink selection with css selector

<div id="welcome-menu" class="panelContainer" style="display: block;">
<ul>
<li>
About
<div class="modal hide" id="displayAbout">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>About</h3>
</div>
<form name="frmSelectEmployees" id="frmSelectEmployees" method="post" action="/index.php/communication/beaconAboutAjax" enctype="multipart/form-data">
<div class="modal-body">
<div id="companyInfo">
<ul>
<li>
<p>Company Name: OrangeHRM (Pvt) Ltd</p>
</li>
<li>
<p>Version: Orangehrm OS 3.3.1</p>
</li>
<li>
<p>Active Employees: 9</p>
</li>
<li>
<p>Employees Terminated: 0</p>
</li>
</ul>
</div>
</div></form>
</div></li>
<!-- <li></li> -->
<li>Logout</li>
</ul>
</div>
Any one knows how to select element with :
<li> Logout </li>.
I have tried following option :
[FindsBy(How = How.CssSelector, Using = "a[href*='/index.php/auth/logout']")]
Also tried link text and tag name. nothing is working. Anyone can help please? thanks
As per the HTML you have shared to identify the element you can use either of the following line of code :
linkText :
[FindsBy(How = How.LinkText, Using = "Logout")]
CssSelector :
[FindsBy(How = How.CssSelector, Using = "li > a[href='/index.php/auth/logout']")]

Navigate with WatiN

I try to automate a Task "Navigating on a Website" with WatiN.
Now, I have the following problem:
The website shows this:
There I have to simulate a click on "Einstellungen". When I look at the Sourcecode it looks like:
<div class="ui-widget ui-opt-outer-nav ui-widget-content ui-corner-all nav_nav_panel">
<div class="ui-widget ui-opt-inner-nav ui-widget-content ui-corner-all">
<div id="NAV" class="nav_nav nav_pointer">
<ul>
<li id="jBtnMENUShipmentList" data-opt-load-type="link"><span><span><label data-opt-label-key="V7GMGLMBTN0120">Sendungsübersicht</label></span></span></li>
<li id="jBtnMENUCreateShipment" data-opt-load-type="link"><span><span><label data-opt-label-key="V7GMGLMBTN0130">Sendung erstellen</label></span></span></li>
<li id="jBtnMENUAddressBook" data-opt-load-type="link"><span><span><label data-opt-label-key="V7GMGLMBTN0140">Adressbuch</label></span></span></li>
<li id="jBtnMENUEndOfDayManifest" data-opt-load-type="menu">
<span class="select-buttons" data-opt-title="">
<span><label data-opt-label-key="V7GMGLMBTN0150">Tagesabschluss</label></span>
</span>
</li>
<li id="jBtnMENUPickup" data-opt-load-type="link"><span><span><label data-opt-label-key="V7GMGLMBTN0160">Abholung</label></span></span></li>
<li id="jBtnMENURateEnquiry" data-opt-load-type="link"><span><span><label data-opt-label-key="V7GMGLMBTN0170">Preisauskunft</label></span></span></li>
<li id="jBtnMENUExpertFunctions" data-opt-load-type="link"><span><span><label data-opt-label-key="V7GMGLMBTN0180">Einstellungen</label></span></span></li>
</ul>
</div>
</div>
</div>
But I have no idea, how I can now with WatiN simulate the click on "Einstellungen".
I have tried:
browser.Link(Find.ByName("jBtnMENUExpertFunctions")).Click();
but with no success.
Comment from jessehouwing is the solution:
The Browser.Link method will search for a Link element, but the element in the DOM you need to click is a Label, so you should probably use:
Browser.Label(Find.ByText("Einstellungen").Click()

Categories