Toggling CSS active classes on click events in Blazor - css

I am trying to find the best way to toggle css classes on and off with Blazor click events. This functionality involves clicking on a column of list boxes generated by a #foreach and changing the style for only the active box and then disabling it with subsequent clicks on other boxes. In short, only the active box changes style on click and all other boxes do not have the style change.
I have started with the code below, which creates the boxes correctly, but it applies 'active' to all of the list boxes rather than to just the currently active box (this is still in POC, so some of the code still needs refactoring):
#foreach (var msg in Message)
{
<a class="list-group-item list-group-item-action #(ActiveClass ? "active" : "")" data-toggle="list" href="#home" role="tab" #onclick="() => popIt(msg.MsgId)">
<span id="msgSubject1">#msg.Subject</span><br /><span class="datetimeCls" id="datetime1">#msg.DateCreated</span>
</a>
}
private bool ActiveClass { get; set; }
public void popIt(int num)
{
var text = Message[num].MessageText;
var subject = Message[num].Subject;
Subject = subject;
MessageText = text;
DateCreated = Message[num].DateCreated;
ActiveClass = true;
}
Angular 8 has the built-in "ngClass." The following code works for this scenario - does Blazor have anything similar? I also noticed that the Angular click can handle two inputs, which I haven't seen in Blazor yet:
<div *ngFor="let msg of messages; let i = index" >
<a class="list-group-item list-group-item-action" data-toggle="list" href="#home" role="tab" [ngClass]="{ 'active' : msg == activeMessage}"(click)="activeMessage = msg;popIt(i)">
<span id="msgSubject1">{{msg.Subject}}</span><br /><span class="datetimeCls" id="datetime1">{{msg.DateCreated | date : 'short' }}</span>
</a>
</div>
</div>

The below code seems to work. It creates a new variable called "ActiveMessageId" and assigns the clicked message id to that message.
#foreach (var msg in Message)
{
<a class="list-group-item list-group-item-action #(ActiveMessageId == msg.MsgId ? "active" : "")" data-toggle="list" href="#home" role="tab" #onclick="() => popIt(msg.MsgId)">
<span id="msgSubject1">#msg.Subject</span><br /><span class="datetimeCls" id="datetime1">#msg.DateCreated</span>
</a>
}
private int ActiveMessageId { get; set; }
public void popIt(int num)
{
var text = Message[num].MessageText;
var subject = Message[num].Subject;
Subject = subject;
MessageText = text;
DateCreated = Message[num].DateCreated;
ActiveMessageId = num;
}

Related

Is there a way to implement asp.net authorization policies in views/front end?

I have applied the asp.net authorization in my views but it seems not to apply. I am using resource. author to display buttons on list.
public class IncidentAuthorizationHandler: AuthorizationHandler<SameAuthorRequirement, Incident>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
SameAuthorRequirement requirement,
Incident resource)
{
if (context.User.Identity?.Name == resource.CreatedBy)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
this is the code in my view but it seems not to work
<td>
<div class="btn-group" role="group">
<button id="btnGroupDrop1" type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Select an Action
</button>
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
#if ((await AuthorizationService.AuthorizeAsync(User, "EditPolicy")).Succeeded)
{
<a class="dropdown-item-custom" asp-action="EditIncident" asp-controller="Incident" asp-route-Id="#i.IncidentId"><i class="fa fa-sm fa-edit text-white mr-3"></i>Edit</a>
}
<a class="dropdown-item-custom" asp-action="ViewIncident" asp-controller="Incident" asp-route-Id="#i.IncidentId"><i class="fa fa-sm fa-comment text-white mr-3"></i>View</a>
<a class="dropdown-item-custom" asp-action="PrintIncident" asp-controller="Incident" asp-route-Id="#i.IncidentId"><i class="fa fa-sm fa-print text-white mr-3"></i>Print</a>
<a class="dropdown-item-custom" asp-action="DeleteIncident" asp-controller="Incident" asp-route-Id="#i.IncidentId"><i class="fa fa-sm fa-trash text-white mr-3"></i>Delete</a>
</div>
</div>
</td>
the user is the owner of the document but the button is not showing. it seems it does not recognize the owner. any assistance is appreciated
the startup looks like this
services.AddAuthorization(options =>
{
options.AddPolicy("EditPolicy", policy => policy.Requirements.Add(new SameAuthorRequirement()));
});
Your issue can be solved by adding this code in your program.cs file:
builer.Services.AddSingleton<IAuthorizationHandler, IncidentAuthorizationHandler>();
For testing purpose, I followed the MS Document created a project, here is my own Handler code:
public class MinimunAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context, MinimumAgeRequirement requirement)
{
var userEmailClaim = context.User.FindFirst(c => c.Type == ClaimTypes.Email);
var userEmail = Convert.ToString(userEmailClaim.Value);
if (userEmail == "123#gmail.com")
{
context.Succeed(requirement);
}
else
{
return Task.CompletedTask;
}
return Task.CompletedTask;
}
}
And here is the code I added to my program.cs file:
builder.Services.AddSingleton<IAuthorizationHandler, MinimunAgeHandler>();
builder.Services.AddAuthorization(options =>{
options.AddPolicy("AtLeast21", policy => policy.Requirements.Add(new MinimumAgeRequirement("123#gmail.com")));});
Ignore the Handler's name, I followed the document and just changed the inside function. Now here is my index page's code:
#using Microsoft.AspNetCore.Authorization
#inject IAuthorizationService AuthorizationService
#{ViewData["Title"] = "Home Page";}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about building Web apps with ASP.NET Core.</p>
</div>
#if ((await AuthorizationService.AuthorizeAsync(User, "AtLeast21")).Succeeded)
{
<p>This paragraph is displayed because you fulfilled PolicyName.</p>
}
At last, here is my test result:
You can see from the picture, when login as "123#gmail.com", the paragraph will be shown.

Calling an action with parameter .Net Core

I have a MyMessages model that I put data into when the user logs in. it includes all the data needed.
for every message that they have I do the following in view
for (int i = 0; i < Model.MyMessagesCount; i++)
{
<li class="list-group-item d-flex align-items-center mt-2">
<a>#Model.MessagesTitles[i]</a>
</li>
When the user clicks on each of the <a>, I want to show them that message in more details in a separate view where I pass MessageID to.
How can I achieve that? How can I have all the <a> call the same action but with different MessageID as a parameter? (Something like this /User/Usermessages?MessageID=20)
You can use anchor tag helper
<li>
<a asp-controller="user"
asp-action="messages"
asp-area=""
asp-route-messageId="#Model.MessagesTitles[i].MessageId">
#Model.MessagesTitles[i]
</a>
</li>
asp-route-{parameter}: the parameter there is the name of the parameter you define in your action.
You can read more on https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/anchor-tag-helper?view=aspnetcore-5.0
In the Index.cshtml page, you could directly set the a tag href attribute.
Code sample as below:
public IActionResult Index()
{
List<MyMessages> messages = new List<MyMessages>()
{
new MyMessages(){ MessageId=1001, MessageTitile="AA"},
new MyMessages(){MessageId =1002, MessageTitile="BB"},
new MyMessages(){MessageId=1003, MessageTitile="CC"}
};
return View(messages);
}
public IActionResult Details(int messageId)
{
return View();
}
Index.cshtml page:
#model List<SignalRChatSample.Models.MyMessages>
<ul>
#for (int i = 0; i < Model.Count(); i++)
{
<li class="list-group-item d-flex align-items-center mt-2">
#Model[i].MessageTitile
</li>
}
</ul>
Then, the result like this (The url: Https://localhost:5001/Home/Details?messageId=1002):
Besides, as David said, you could also send the parameters via the route.

how to add and remove a style class on anchor tag

I am struggling on adding and removing a style class on a anchor tag. I want to add a active class when I clicked on that tag and remove active from last active one. By default Home page would be active. I have tried many things but no success on that!
I have added few code but it is not working. Below is the code:
angularjs code:
$scope.isActive = false;
$scope.getCSSClass = function () {
return 'active';
}
<div id="mySidenav" class="sidenav">
<span class="whitelogo"><img src="styles/images/logo-small.png" alt="logo"></span>
<div class='clearfix'></div>
×
<a ng-link="['/Home/']" title="Home" class="active"><i class="fas fa-home"></i>Home</a>
<a ng-link="['/TestForm/']" ng-class="getCSSClass()" title="Application Form"><i class="fas fa-user-edit"></i> test Form</a>
</div>
Any help will be appreciated. Thanks in advance!
On Angularjs file, I have added this code:
$scope.getStyleClass = function (path) {
var cur_path = $location.path().substr(0, path.length);
if (cur_path == path) {
if ($location.path().substr(0).length > 1 && path.length == 1)
return "";
else
return "active";
} else {
return "";
}
}
And on Html side:
ng-class="getStyleClass('/TestForm')"

Issue with pagination in PagedList MVC

I am using MVC PagedList to handle pagination in my project. Everything works fine except that in my controller I have to initialize pageNumber parameter with "1" (Default page number). The problem is the blue activated button on page 1 that remains there no matter which page button link you click. Moreover this button is also missing the actionLink.
One last thing I am integrating my search with pagination as you can see in the controller code.
public PartialViewResult List_Items(string keyword, int? page)
{
var newlists = from s in db.TrMs
orderby s.S1
select s;
int pageNumber = (page ?? 1);
int pageSize = 10;
var data = newlists.Where(f => f.S1.ToString().Contains(keyword) && f.S100 == vt).ToPagedList(pageNumber, pageSize);
return PartialView(data);
}
Here is the generated Html for PagedList in my view
<div class="pagination-container">
<ul class="pagination">
<li class="active">
<a>1</a>
</li>
<li>
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#searchResult" href="/TrM/Search_Items?keyword=&page=2">2</a>
</li>
<li>
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#searchResult" href="/TrM/Search_Items?keyword=&page=3">3</a>
</li>
<li>
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#searchResult" href="/TrM/Search_Items?keyword=&page=4">4</a>
</li>
<li class="PagedList-skipToNext">
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#searchResult" href="/TrM/Search_Items?keyword=&page=2" rel="next">»</a>
</li>
</ul>
</div>
Here is how I am using the pagination helper.
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("List_Items", "TrM", new {keyword="", page }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = "searchResult" }))
Your paging controls are most likely outside of the searchResult container. When you click on a page you're replacing the table but not the paging controls. Move your paging controls inside of your searchResult container!
Hope this helps.

Getting StaleElementReferenceException after performing checkbox.click()

Getting org.openqa.selenium.StaleElementReferenceException: Element is no longer attached to the DOM
list = driver.findElements(By.cssSelector(listLocator));
for (WebElement listItem : list) {
checkbox = listItem.findElement(By.cssSelector(checkboxLocator));
checkbox.click();
String path = checkbox.getCssValue("background-image"));
}
After performing checkbox.click(); I am not able to call any method on checkbox element
corresponding image :
My Locators are
listLocator = ul[class="planList"] > li[class="conditionsTextWrapper"]
checkboxLocator = label[role="button"] > span[class="ui-button-text"]
My HTML source before peforming checkbox.click() :
<ul class="planList">
<li class="conditionsTextWrapper" >
<input name="chkSubOpt" type="checkbox">
<label class="check ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" for="CAT_5844" aria-pressed="false" role="button">
<span class="ui-button-text"></span>
</label>
<label class="conditionsText">Eat at least 5 fruits and vegetables every day</label>
</li>
</ul>
after performing checkbox.click() :
<ul class="planList">
<li class="conditionsTextWrapper" >
<input name="chkSubOpt" type="checkbox">
<label class="check ui-button ui-widget ui-state-default ui-corner-all ui-state-active ui-button-text-only" for="CAT_5844" aria-pressed="true" role="button" aria-disabled="false">
<label class="conditionsText">Eat at least 5 fruits and vegetables every day</label>
</li>
</ul>
As mentioned above, the reason of these errors is that the DOM structure has been changed after clicking on the checkbox. The following code works for me.
string checkboxXPath = "//input[contains(#id, 'chblRqstState')]";
var allCheckboxes = driver.FindElements(By.XPath(checkboxXPath));
for (int i = 0; i != allCheckboxes.Count; i++)
{
allCheckboxes[i].Click();
System.Threading.Thread.Sleep(2000);
allCheckboxes = driver.FindElements(By.XPath(checkboxXPath));
}
Your DOM is changing following the .click(), as such the reference Webdriver formed to relate to that element (as in the next in your list) is no longer valid. As such you're going to need to rebuild the list within your loop.
list = driver.findElements(By.cssSelector(listLocator));
for (i=0; list.length(); i++) {
list = driver.findElements(By.cssSelector(listLocator));
checkbox = list[i].findElement(By.cssSelector(checkboxLocator));
checkbox.click();
String path = checkbox.getCssValue("background-image"));
}
This Happens because your DOM structure has changed since you have referred your check box.
This is a very common Exception which people get.
WorkAround can be to catch the Exception and try locating and clicking the same element again.
Example
WebElement date = driver.findElement(By.linkText("date"));
date.click();
}
catch(org.openqa.selenium.StaleElementReferenceException ex)
{
log.debug("Exception in finding date");
log.debug(e);
WebElement date = driver.findElement(By.linkText("date"));
date.click();
}
This can solve most of your problems !
Will work for your checkbox problem as well. However i suggest you use #Mark Rowlands solution. His code is cleaner.

Resources