Button in MudBlazor table not routing to different page - c#

I’m new with both Blazor and MudBlazor. I was following along with a video on MudBlazor and I like how it can quickly have both filtering and sorting. However, now I’m trying to have some buttons for in the table row for CRUD operations on a record. I can’t seem to get it to route to another page nor have a been able to find a way to pass the record ID to a function. I’ve tried both a MudButton and a Bootstrap button and can’t get either to work.
In the code below I have a button that I would like to a different page as well as an Edit button I would like to navigate to another page while passing along the record ID. When I run this and try to go to the Groups button nothing happens. No errors, just not routing to another page.
Anyone know what I’m missing or maybe a example I can follow?
<MudTable Items="#listApplications" Dense="true" Hover="true" Bordered="true" Striped="true"
Filter="new Func<Application,bool>(FilterFunc1)" #bind-SelectedItem="selectedApplication">
<ToolBarContent>
<MudTextField #bind-Value="searchString" Placeholder="Search" Adornment="Adornment.Start" AdornmentIcon="#Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
</ToolBarContent>
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Description</MudTh>
<MudTh></MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Name">#context.Name</MudTd>
<MudTd DataLabel="Description">#context.Description</MudTd>
<MudTd>
<button type="button" class="btn btn-secondary btn-sm" onclick="GoToGroups()">Groups</button>
<button class="btn btn-primary btn-sm" >
Edit
</button>
<button type="button" class="btn btn-danger btn-sm">Delete</button>
</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
#code {
private List<Application>? listApplications;
private string searchString = "";
private Application selectedApplication = null;
protected override async Task OnInitializedAsync()
{
listApplications = await _db.GetApplications();
}
private bool FilterFunc1(Application app) => FilterFunc(app, searchString);
private bool FilterFunc(Application app, string searchString)
{
if (string.IsNullOrWhiteSpace(searchString))
return true;
if (app.Description.Contains(searchString, StringComparison.OrdinalIgnoreCase))
return true;
if (app.Name.Contains(searchString, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
void ShowApplication(int id)
{
NavigationManager.NavigateTo($"ApplicationMgmt/{id}");
}
void CreateNewApplication()
{
NavigationManager.NavigateTo("ApplicationMgmt");
}
void GoToGroups()
{
NavigationManager.NavigateTo("Groups");
}
}

In Blazor you specify an event handler using the syntax #on{DOM EVENT}="{DELEGATE}".
So you need to use #onclick="GoToGroups" instead of onclick="GoToGroups()".
Blazor event handling
To pass parameters you can use this syntax: #onclick="() => ShowApplication(100)". So for your example:
<RowTemplate>
<MudTd DataLabel="Name">#context.Name</MudTd>
<MudTd DataLabel="Description">#context.Description</MudTd>
<MudTd>
<button type="button" class="btn btn-secondary btn-sm" #onclick="GoToGroups">Groups</button>
<button class="btn btn-primary btn-sm" #onclick="() => ShowApplication(context.Id)">
Edit
</button>
<button type="button" class="btn btn-danger btn-sm">Delete</button>
</MudTd>
</RowTemplate>

Related

ASP.NET paging is reset with multiple forms

My web interface has a control panel built in Razor Pages, showing varius devices admins can configure, displayed in a table with varius buttons on the side for quick actions and info (like a toggle to show enabled and connection status).
The way i built it initially it had no paging, and a simple loop with a forms column for quick actions, based on the relative row
#{
ViewBag.BaseLayout = "/Views/Shared/_Layout_Areas.cshtml";
Layout = "/Views/Shared/_Layout_IPageable.cshtml";
}
#* other code bla bla bla *#
#foreach (var device in #Model.Devices)
{
<tr>
<td class="forms-inline">
<form id="entrydelete" method="post" onsubmit="return _formconfirm();">
<button class="btn btn-danger btn-sm" type="submit" asp-page-handler="delete" asp-route-id="#device.Id">
<i class="bi bi-trash3"></i>
</button>
</form>
<form id="useredit" method="post">
<button class="btn btn-warning btn-sm d-inline" type="submit" asp-page-handler="update" asp-route-id="#device.Id">
<i class="bi bi-pencil-square"></i>
</button>
</form>
<form id="toggle" method="post" target="_self">
<button class="btn #(device.Enabled ? "btn-success" : "btn-secondary" ) btn-sm d-inline" type="submit" asp-page-handler="toggleEnabled" asp-route-id="#device.Id">
#if (device.Enabled)
{
<i class="bi bi-toggle-on"></i>
}
else
{
<i class="bi bi-toggle-off"></i>
}
</button>
</form>
</td>
<td>#Html.Label("Description",device.Description ?? "---")</td>
<td class="font-monospace w-space-pre">#Html.Label("Ip",device.Ip)</td>
<td class="font-monospace">#Html.Label("Port",device.Port.ToString())</td>
<td>#Html.Label("Organization",device.OwnedBy?.Name)</td>
<!-- etc. etc. -->
</tr>
}
Now this works great if you have no paging and stuff... But later on as devices grew exponentially a paging solution needed implementation.
So i added an IPageable interface and implemented extension methods and stuff, but i added an additional layout Layout_IPageable.cshtml adding the paging controls at the bottom of the page in the classic
[<-] [1] ... [n-1] [n] [n+1] ... [nmax] [->]
for reusability and style consistency across multiple pages, but i made it a form for general filtering for specific pages
_Layout_IPageable.cshtml
#model IPageable
#{
Layout = ViewBag.BaseLayout ?? "/Views/Shared/_Layout.cshtml";
}
<!-- render body and styles and stuff -->
<div class="paging form text-end">
<label asp-for="Paging.PageNumber" class="form-label"></label>
#if (Model.Paging.PageNumber > 1)
{
<button class="btn btn-sm btn-secondary" onclick="navigatePaging(-1)"><i class="bi bi-arrow-left"></i></button>
}
else
{
<button class="btn btn-sm btn-outline-secondary" disabled><i class="bi bi-arrow-left"></i></button>
}
<!-- etc. etc. -->
[ <input asp-for="Paging.PageNumber" style="width: min-content" form="search" min="1" max="#Model.Paging.MaxPageCount" onchange="this.form.submit()" />
/#Html.Label(Model.Paging.MaxPageCount.ToString()) ]
<!-- etc. etc. -->
<label asp-for="Paging.Take" class="form-label"></label>
<select asp-for="Paging.Take" form="search" onchange="this.form.submit()">
<option>10</option>
<option>25</option>
<option>40</option>
<option>100</option>
</select>
</div>
interface IPageable
{
DataPagingModel Paging { get; set; }
}
public static class PagingExtensions
{
public static IEnumerable<T> Page<T>( this IEnumerable<T> input, DataPagingModel paging )
{
paging.ResultsCount = input.Count();
paging.MaxPageCount = (input.Count()-1) / paging.Take + 1;
paging.PageNumber = Math.Min(paging.PageNumber, paging.MaxPageCount);
return input
.Skip(paging.Skip)
.Take(paging.Take);
}
}
[ValidateAntiForgeryToken]
public partial class DevicesModel : PageModel, IPageable
{
[BindProperty(SupportsGet = true)]
public DataPagingModel Paging { get; set; }
public IActionResult OnGet()
{
thid.Devices = this._devicesSrvc
.VariusChainedLinqMethods()
.Page(this.Paging);
return this.Page();
}
public async Task<IActionResult> OnPostUpdate( Guid id )
{
/* code to update and stuff ... */
// the problem is here, in non-get methods i cannot figure out
// how to return the correct paging because this.Paging is null!
return this.OnGet();
}
/* etc. etc. */
}
The problem is that now when previus "quick actions" forms are submitted, obviusly the paging form is NOT submitted as well, and the paging resets to the default (so page 0, with default page size and no filters).
How should i go about this?
Implement the previus "quick actions" forms as API calls and then reload the page?
Or is there a more elegant solution?
The solutions was pretty simple actually, because the original query is sent by the browser as an URL in the Referer HTTP Header when submitting any form.
So when sending one of the "post" forms, i expect the referer to be the original GET query for the page.
Given it's not something to completely rely on, but returning this at the end of the varius Post handlers...
protected IActionResult RedirectReferOrReload( )
{
var referer = this.Request.Headers.Referer.SingleOrDefault();
if (string.IsNullOrEmpty(referer))
return this.RedirectToPage();
// this ensures the referer cannot go to a malicius link
return this.Redirect(this.Request.Path.Value + new Uri(referer).Query);
}
public async Task<IActionResult> OnPostUpdate( Guid id )
{
/* code to update and stuff ... */
return this.RedirectReferOrReload();
}
... will redirect to the referer page if the header is present, otherwise just reload the page.
When the page is then reloaded with the correct query the status of the shown items is correctly updated.
Now, this works great for my case because i expect default browser configurations from the target users of this page, so that the Referer header is always sent on forms (i also updated ASP.NET to specify a Referrer-Policy).

Post form with only input buttons passing parameters to Action in ASP.NET Core

Im working on my thesis work, im making a simple browser game, so far I have a form with 3 buttons that I want to invoke the same action with diffirent parameters this is what i've figured so far:
#using (Html.BeginForm("Gather", "Character", FormMethod.Post, new { #class = "btn-group-vertical mr-2", #role = "group", }))
{
<h3>Woods:</h3>
<input type="submit" class="btn btn-secondary" value="Woods of Deloria (90%)" />
#Html.Hidden("area", "woods")
#Html.Hidden("type", "deloria")
<input type="submit" class="btn btn-secondary" value="Woods of Forgotten souls (50%)" />
#Html.Hidden("area", "woods")
#Html.Hidden("type", "forgotten souls")
<input type="submit" class="btn btn-secondary" value="Shadowforest (10%)" />
#Html.Hidden("area", "woods")
#Html.Hidden("type", "shadowforest")
}
My question is how do i make the diffirent buttons to pass diffirent types, all buttons have to be in same form otherwise it breaks my css. I am using hidden, since I don't want the user to be able to edit the values that are passed as parameters, also I don't want to values to be passed onto the URL.
EDIT: I realized that #Html.Hidden doesn't hide it from the html, what would my approach be if i want to pass parameters to an action from a button, that the user can't edit?
EDIT 2: Alright so i made some progress, changed the form to
<form method="post" class="btn-group-vertical mr-2" role="group">
<h3>Woods:</h3>
<input type="submit" class="btn btn-secondary" name="deloria" value="Woods of Deloria (90%)" />
<input type="submit" class="btn btn-secondary" name="forgotten souls" value="Woods of Forgotten souls (50%)" />
<input type="submit" class="btn btn-secondary" name="shadowforest" value="Shadowforest (10%)" />
</form>
And my post action looks like this:
[HttpPost]
public IActionResult Gather(int id)
{
var taskName = "";
if (Request.Form.ContainsKey("deloria"))
{
taskName = "deloria";
}
else if (Request.Form.ContainsKey("forgotten souls"))
{
taskName = "forgotten souls";
}
else if (Request.Form.ContainsKey("shadowforest"))
{
taskName = "shadowforestD";
}
if (string.IsNullOrEmpty(taskName))
{
return Json("uh oh");
}
else
{
return Json(taskName);
}
}
}
I know it's a mess, but i will find a way to make it a little more compact.
I would take a look at the various options outlined here:
http://www.binaryintellect.net/articles/c69d78a3-21d7-416b-9d10-6b812a862778.aspx
If you are using Razor Pages, and not traditional MVC, you should take a look at handler methods:
https://www.learnrazorpages.com/razor-pages/handler-methods

Selenium Click a Button Without ClassName/ID

I am developing a Browser Automation, but I can't click a button. I have tried this procedure:
webDriver.FindElement(By.XPath("//*[#id='uploadForm: fileup']/div[1]/button[1]")).Click();
webDriver.ElementBul(By.("Dosya Yükle"), gecisScreen).Click();
HTML
<a type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-icon-left ui-fileupload-upload" role="button" aria-disabled="true">
<span class="ui-button-icon-left ui-icon ui-c ui-icon-arrowreturnthick-1-n"></span><span class="ui-button-text ui-c">Dosya Yükle</span>
</a>
As follows:
Try this:
foreach (IWebElement item in webDriver.ElementleriBul(By.XPath("//button//span"), gecisScreen))
{
if (item.Text.In("Yükle"))
{
item.Tikla();
break;
}
}

Cant get values of inputs asp.net

My problem i always get null from my inputs or default value. Some how if i set value at page_load like Form_txt_Ad.Value="ExampleValue"; i can get it. But i cant get any value from inputs.
protected void Save_Button_Click(object sender, EventArgs e)
{
string exapmle = Form_txt_Ad.Value;
string example = Form_txt_Soyad.Value;4
}
<div class="input">
<input type="text" translate translate-attr-placeholder=".PLACEHOLDER_NAME" placeholder="Ad" id="Form_txt_Ad" runat="server" />
<span><i class="glyphicon glyphicon-user"></i></span>
</div>
<div class="col-md-12" style="text-align: center;">
<button type="button" runat="server" onserverclick="Save_Button_Click" class="btn btn-success btn-raised btn-lg" title="Kaydet"><i class="glyphicon glyphicon-floppy-saved icon-marginRight"></i>Kaydet</button>
</div>
Thx for help.
Make sure all your control elements are placed inside <form> ... </form> tag.
Since you have placed runat="server" you should be able to get the value by either using any of them
Form_txt_Ad.Value
(OR)
Form_txt_Ad.Text
Else use Request.Form["Form_txt_Ad"]
Not sure though why not use a server side control using <asp:TextBox ... which will allow you to get the textbox value directly using the Text property
Add the name attribute to your input and make sure it's inside a form element.
<form>
...
<input type="text" translate translate-attr-placeholder=".PLACEHOLDER_NAME" placeholder="Ad" id="Form_txt_Ad" name="Form_txt_Ad" runat="server" />
...
</form>

Using ActionLink parameters with button onclick ?

Straight forword I think just can't find the syntax...
I have an ActionLink which uses an id property,
<td style="padding-right:35px">#Html.ActionLink("Edit", "EditUser", new { #id = user.ID.ToString("N") }, new { #class = "btn yellow"})</td>
Basically I want to change this to a btn, but I am not sure how to pass the id parameter , I think it's something like this....
<button type="button" class="btn blue" id="user.ID.ToString("N")" onclick="location.href='ReferralTarget/EditUser'"><i class="icon-edit"> Edit</button>
Where ReferralTarget is my control and EditUser my actionResult..
Please note this is an ASP.NET MVC4 application using Razor2 views...
Try something like this:
<button type="button" class="btn blue" onclick="location.href='#Url.Action("EditUser", "ReferralTarget", new { #id = user.ID }'"><i class="icon-edit"> Edit</button>
You could also do something like this if you like the jquery route:
#functions
{
private string GetUserId()
{
//I'm not sure where you are getting user from, but if it's from the model,
//you would need to change this to something like #Model.something....
return user.ID;
}
}
<script type="text/javascript">
$(document).ready(function() {
$('#edit').click(function() {
location.href = "ReferralTarget/EditUser/" + "#GetUserId()";
});
});
</script>
<input type="button" id="edit" value="edit" />
There is an easy trick-
Use jqueryUi, and justdo $("#linkid").button(), and style it how ever u want

Categories

Resources