Change the HTML element in a Blazor application by pressing a button? - html

I wanted to ask if it is possible to change the type of an HTML element in a Blazor application by pressing a button.
That I turn a normal text into a text input field for example.
I have a table where the first column has a button for each entry in the table.
My goal is that when you press the button, the fields turn into input fields in time, so you can edit the values.
This is just a quick photo edit, but this is how I imagined it.

You can use an index array to track the edit status of each company. When the button is clicked, the corresponding value in the index array will be toggled. If the value is set to true, the cell will display an input field where you can edit the company name. The updated name will be saved to the list.
Instead of using a simple string to store company information, you can create a class that contains multiple properties such as the company name and location..
Output:
Demo: https://blazorfiddle.com/s/lil3olrf
Implementation:
#page "/"
<h3>Companies</h3>
<table class="table table-bordered table-sm">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Company</th>
<th scope="col">Location</th>
</tr>
</thead>
<tbody>
#foreach (var company in Companies)
{
var index = Companies.IndexOf(company);
<tr>
<td>
<button type="button" class="btn btn-primary"
#onclick="#(() => { Edits[index] = !Edits[index]; })">
#(Edits[index] ? "Back" : "Edit")
</button>
</td>
<td>
#if (Edits[index])
{
<input class="form-control" type="text"
style="background-color:lightyellow;"
value="#company.Name"
#oninput="#(e => { Companies[index].Name = e.Value.ToString(); })"/>
}
else
{
#company.Name
}
</td>
<td>
#if (Edits[index])
{
<input class="form-control" type="text"
style="background-color:lightyellow;"
value="#company.Location"
#oninput="#(e => { Companies[index].Location = e.Value.ToString(); })"/>
}
else
{
#company.Location
}
</td>
</tr>
}
</tbody>
</table>
<ul>
#*Check realtime changes to Company names when you edit them*#
#foreach (var company in Companies)
{
<li>#company.Name: #company.Location</li>
}
</ul>
#code {
private List<Company> Companies { get; set; } = new List<Company>
{
new Company("Globex Corporation", "Germany"),
new Company("Soylent Corp", "Switzerland"),
new Company("Umbrella Corporation", "Netherlands")
};
private bool[] Edits { get; set; }
protected override void OnInitialized()
{
Edits = new bool[Companies.Count];
}
public class Company
{
public Company(string name, string location)
{
Name = name;
Location = location;
}
public string Name { get; set; }
public string Location { get; set; }
}
}

Here's a basic component and demo page to demonstrate how you can do this. There's a component and a demo page.
#page "/"
<div>
<button class="btn btn-dark" disabled="#isEdit" #onclick="GoEdit">Edit</button>
#if (this.isEdit)
{
#EditControl
}
else
{
#ViewControl
}
</div>
#code {
[Parameter] public RenderFragment ViewControl { get; set; }
[Parameter] public RenderFragment EditControl { get; set; }
protected string disabled => isEdit ? "disabled": "";
protected bool isEdit { get; set; }
private void GoEdit(MouseEventArgs e)
=> isEdit = !isEdit;
}
And a demo page:
#page "/Demo"
<h3>EditorTest</h3>
<CascadingValue Value="model">
<EditForm EditContext="editContext">
<EditFormState #ref="editFormState" EditStateChanged="EditStateChanged"></EditFormState>
<div>
<SwitchedEditorComponent>
<EditControl>
Email: <InputText #bind-Value="model.Email" placeholder="Enter your Email Address"></InputText>
</EditControl>
<ViewControl>
Email: <InputText #bind-Value="model.Email" disabled></InputText>
</ViewControl>
</SwitchedEditorComponent>
</div>
</EditForm>
</CascadingValue>
#code {
private dataModel model { get; set; } = new dataModel();
private EditFormState editFormState;
private EditContext editContext;
protected override Task OnInitializedAsync()
{
this.editContext = new EditContext(model);
return base.OnInitializedAsync();
}
private void EditStateChanged(bool editState)
{
StateHasChanged();
}
public class dataModel
{
public string Email { get; set; }
}
}

Related

Returning the property of another model that is bound to a Model in the first model

I'm trying to access the Question list from within the TestModel and return the QuestionModel's Property.
I need to return it in the TestModel to determine the questions to be contained in a test, but I can't, I can't find where I went wrong.
public class TestModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<QuestionModel> Question { get; set; }=new List<QuestionModel>();
}
public class QuestionModel
{
public int Id { get; set; }
public AppUser User { get; set; }
public string Question { get; set; }
public bool check { get; set; }
public List<TestModel>? Tests { get; set; } = new List<TestModel>();
}
I used TestModel as one of the tuple objects.
Since the code is long, I only shared the area where I had problems.
#model Tuple<IList<TestModel>,IList<UserViewModel>>
<table class="table table-striped">
#{
int i = 0;
}
#foreach (var item in Model.Item1)
{
<form id="form[#i]" class="container m-xl-2">
<tr id="Allquest_#i" hidden="hidden">
<td colspan="5">
<div>
<input asp-for=#Model.Item1[i].Id hidden>
#for (int indexQuest = 0; indexQuest < item.Question.Count; indexQuest++)
{
<input type="checkbox" asp-for="#Model.Item1[i].Question[indexQuest].check" checked="#Model.Item1[i].Question[indexQuest].check">
<a>#Model.Item1[i].Question[indexQuest].Question</a>
<br />
}
</div>
</td>
<td>
<button type="submit" asp-controller="Test" asp-action="DeleteQuestionToTest" formmethod="post" class="btn btn-danger">Seçili Soruları Sil</button>
</td>
</tr>
</form>
i++;
}
Here I tried to capture it as a TestModel. I tried to capture it as a list and as a single model, but I couldn't.
[HttpPost]
public IActionResult DeleteQuestionToTest([Bind(Prefix ="Item1")] IList<TestModel> tests)
{
return View();
}
How can I fix the code?
I came up with a workaround by creating a separate form and specifying it in the html of the models.
#model Tuple<IList<TestModel>,IList<UserViewModel>>
<table class="table table-striped">
#{
int i = 0;
}
<form id="MainForm" asp-controller="Test" asp-action="DeleteQuestionToTest" formmethod="post"></form>
#foreach (var item in Model.Item1)
{
<form id="form[#i]" class="container m-xl-2">
<tr id="Allquest_#i" hidden="hidden">
<input asp-for=#Model.Item1[i].Id form="MainForm" hidden>
<td colspan="5">
<div>
#for (int indexQuest = 0; indexQuest < item.Question.Count; indexQuest++)
{
<input asp-for="#Model.Item1[i].Question[indexQuest].Id" form="MainForm" hidden>
<input type="checkbox" asp-for="#Model.Item1[i].Question[indexQuest].check" form="MainForm" checked="#Model.Item1[i].Question[indexQuest].check">
<a>#Model.Item1[i].Question[indexQuest].Question</a>
<br />
}
</div>
</td>
<td>
<button type="submit" form="MainForm" class="btn btn-danger">Seçili Soruları Sil</button>
</td>
</tr>
</form>
i++;
}
I returned all the tests this was a workaround, I have no idea how to return a single test.

ASP.NET Core : moving data from one table into another

I'm currently developing an e-commerce website using ASP.NET Core. I'm trying to move data from the Products table into the Items table using a button press so that the data in the items table can be presented on the basket page. Any help would be appreciated.
The relevant button is using the asp-action=AddToCart:
<div class="card-body">
<h3 class="card-title">#product.Name</h3>
<svg class="bd-placeholder-img card-img-top" width="100%" >
<img float="left" width="450" height="450" src="~/assets/Clothing/#product.ProductImage" alt="Image of #product.Name" />
</svg>
<p class="card-text">#product.Description</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
#if (User.IsInRole("Admin"))
{
<a class="btn btn-sm btn-outline-secondary" asp-page="/UpdateProductInfo"
asp-route-id="#product.Id">Update</a>
}
<a class="btn" asp-action="AddToCart">Add to Cart</a>
<p class="float-right card-text" >£#product.Price</p>
</div>
</div>
</div>
C#
[BindProperty]
public Item Items { get; set; }
public IActionResult addToCart()
{
_db.Items.Add(Items);
_db.SaveChanges();
return Page();
}
product.cs model
public class Product
{
public String Id { get; set; }
[ForeignKey("Name")]
public String Name { get; set; }
public String Description { get; set; }
[ForeignKey("Price")]
public int Price { get; set; }
[ForeignKey("ProductImage")]
public String ProductImage { get; set; }
public String ProductImageName { get; set; }
}
Item.cs model
public class Item
{
[Key]
[ForeignKey("Name")]
public String Name { get; set; }
[ForeignKey("Price")]
public int Price { get; set; }
[ForeignKey("ProductImage")]
public String ProductImage { get; set; }
}
item class must be like this :
public class Item
{
[Key]
public int ItemId { get; set; }
public Product Product { get; set; }
[ForeignKey("Product")]
public int ProductId { get; set; }
public String ProductImage { get; set; }
}
and your product class :
public class Product
{
[Key]
public int ProductId { get; set; }
public string Name{ get; set; }
public int Price { get; set; }
public int Description{ get; set; }
public string ProductImage { get; set; }
public string ProductImageName { get; set; }
}
it was a (single to single) relationship between two tables
I'm trying to move data from the Products table into the Items table
using a button press so that the data in the items table can be
presented on the basket page. Any help would be appreciated.
From your description, I assume in the main page, you are listed the Products and each row of the product will have an "Add to Cart" button, after clicking it, it will add the selected Product to the Item table, right?
Since your application is an Asp.net core Razor Page application, for the "Add to Cart" button, you could use the handler method to transfer the route parameter to the method, and then add the selected product to Item table. Refer the following sample:
Model:
public class Product
{
public String Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public int Price { get; set; }
public String ProductImage { get; set; }
public String ProductImageName { get; set; }
}
public class Item
{
[Key]
public String Name { get; set; }
public int Price { get; set; }
public String ProductImage { get; set; }
}
ProductIndex.cshtml:
#page
#model RazorPageSample.Pages.ProductIndexModel
<div class="row">
<div class="col-md-6">
<form method="post">
<table class="table">
<thead>
<tr><th colspan="5">Product Table</th></tr>
<tr>
<th>
Product ID
</th>
<th>
Product Name
</th>
<th>
Description
</th>
<th>
Price
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Products)
{
<tr>
<td>
#item.Id
</td>
<td>
#item.Name
</td>
<td class="text-right">
#item.Description
</td>
<td class="text-right">
£#item.Price
</td>
<td class="text-right">
<button asp-page-handler="AddToCart" class="btn btn-primary" asp-route-id="#item.Id">Add to Cart</button>
</td>
</tr>
}
</tbody>
</table>
</form>
</div>
<div class="col-md-6">
<table class="table">
<thead><tr><th colspan="5">Item Table</th></tr>
<tr>
<th>
Item Name
</th>
<th>
Price
</th>
<th></th>
</tr>
</thead>
<tbody>
#if (Model.Items !=null && Model.Items.Count > 0)
{
foreach (var item in Model.Items)
{
<tr>
<td>
#item.Name
</td>
<td class="text-right">
£#item.Price
</td>
<td class="text-right">
</td>
</tr>
}
}
</tbody>
</table>
</div>
</div>
ProductIndex.cshtml.cs:
public class ProductIndexModel : PageModel
{
//Here I use a DataRepository to set the initial data.
private readonly IDataRepository _repository;
public ProductIndexModel(IDataRepository repository)
{
_repository = repository;
}
[BindProperty]
public List<Item> Items { get; set; } //item list.
public List<Product> Products { get; set; } // product list
public void OnGet()
{
Products = _repository.GetProducts();
}
public async Task<IActionResult> OnPostAddToCartAsync(string id)
{
// use session to store the items.
// you could directly store the new item into database. in this scenario, you could query the database and get the existing items.
if (HttpContext.Session.Get<List<Item>>("_Items") != default)
{
Items = (List<Item>)HttpContext.Session.Get<List<Item>>("_Items");
}
if (id != null)
{
var product = _repository.GetProducts().Where(c => c.Id == id).FirstOrDefault();
Item newitem = new Item()
{
Name = product.Name,
Price = product.Price
};
Items.Add(newitem);
HttpContext.Session.Set<List<Item>>("_Items", Items);
}
//reset the Products.
Products = _repository.GetProducts();
return Page();
}
}
The screenshot as below:
More detail information, you could refer the following links:
Handler Methods in Razor Pages
Write a basic form
Session and state management in ASP.NET Core
[Note]If you want to use Session to store the Object,after configuring your application use session, remember to add SessionExtensions.

Razor Page: RedirectToPage with Parameters

I have a razor page which displays company and list of staff:
#page "/admin/companies/editor/{id}"
#model EditorModel
#{
}
<h1>admin . companies . editor</h1>
<h4>Id = #Model.Id</h4>
<h4>Name = #Model.OrgStructure.Organisation.Name</h4>
<h4>Staff Count = #Model.OrgStructure.Staff.Count</h4>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
#if (Model.OrgStructure.Staff.Any())
{
foreach (Staff s in Model.OrgStructure.Staff)
{
<tr>
<td>#s.Id</td>
<td>#s.Username</td>
</tr>
}
}
else
{
<tr>
<td colspan="2">No Staff</td>
</tr>
}
</table>
<a class="btn btn-primary" asp-page="/admin/companies/staff/create" asp-route-orgId="#Model.OrgStructure.Organisation.Id">Create Staff</a>
#functions {
public class EditorModel : PageModel
{
public string Id { get; set; }
public OrganisationStructure OrgStructure { get; set; }
public void OnGet(string id)
{
Id = id;
using (DirectoryApp app = ServicesFactory.MakeDirectoryService())
{
OrgStructure = app.GetOrganisationStructureAsync(new Guid(id)).Result;
}
}
}
}
When I click:
<a class="btn btn-primary" asp-page="/admin/companies/staff/create" asp-route-orgId="#Model.OrgStructure.Organisation.Id">Create Staff</a>
It takes me to my staff creation page:
#page "/admin/companies/staff/create/{orgId}"
#model CreateModel
#{
}
<h1>admin . companies . staff . create</h1>
<h3>OrganisationId = #Model.OrganisationId</h3>
<form method="post">
<input name="OrgId" value="#Model.OrganisationId" />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label>User Name</label>
<input name="UserName" class="form-control" value="#Model.UserName" />
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<a class="btn btn-secondary" asp-page="/admin/companies/editor" asp-route-id="#Model.OrganisationId">Back</a>
</form>
#functions {
public class CreateModel : PageModel
{
[BindProperty]
[Required]
public string UserName { get; set; }
public Guid OrganisationId { get; set; }
public void OnGet(string orgId)
{
OrganisationId = new Guid(orgId);
}
public async Task<IActionResult> OnPostAsync(string userName, string orgId)
{
if (ModelState.IsValid)
{
using (DirectoryApp app = ServicesFactory.MakeDirectoryService())
{
await app.AddStaffToOrganisationAsync(
new Guid(orgId),
new Staff()
{
Username = userName
});
return RedirectToPage("/admin/companies/editor/" + orgId.ToString());
}
}
return Page();
}
}
}
I have 2 questions:
If I click Submit and have a Model error then I get the error message but the fields which are using #Model.OrganisationId are blanked out. I guess that when OrganisationId is set in the OnGet() method, this is lost after a post. Does this mean I'd need to repopulate my CreateModel.OrganisationId in the OnPostAsync() method?
If I am successful the line:
return RedirectToPage("/admin/companies/editor/" + orgId.ToString());
should take me back to the original screen viewing the company with the newly added staff member. However, I am getting an error:
An unhandled exception occurred while processing the request.
InvalidOperationException: No page named '/admin/companies/editor/473be1aa-7d62-4964-b744-b34e0489d7ad' matches the supplied values.
But, if I copy and paste the "/admin/companies/editor/473b..." into the address bar in the browser after "localhost:12345" then the page opens no problem.
What am I doing wrong?
UPDATE
I need to replace:
return RedirectToPage("/admin/companies/editor/" + orgId.ToString());
with
return RedirectToPage("/admin/companies/editor", new { id = orgId.ToString() });
If I click Submit and have a Model error then I get the error message
but the fields which are using #Model.OrganisationId are blanked out.
I guess that when OrganisationId is set in the OnGet() method, this is
lost after a post. Does this mean I'd need to repopulate my
CreateModel.OrganisationId in the OnPostAsync() method?
The answer to this was:
Instead of declaring arguments in the OnPostAsync method, I should use the properties in the CreateModel itself (with binding).
Use [BindProperty] on OrganisationId property in CreateModel.
On the input control, change name of OrgId field to OrganisationId.
So:
<input name="OrgId" value="#Model.OrganisationId" />
Changes to:
<input name="OrganisationId" value="#Model.OrganisationId" />
AND
public async Task<IActionResult> OnPostAsync(string userName, string orgId)
{
if (ModelState.IsValid)
{
using (DirectoryApp app = ServicesFactory.MakeDirectoryService())
{
await app.AddStaffToOrganisationAsync(
new Guid(orgId),
new Staff()
{
Username = userName
});
return RedirectToPage("/admin/companies/editor/" + orgId.ToString());
}
}
return Page();
}
Changes to:
public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
using (DirectoryApp app = ServicesFactory.MakeDirectoryService())
{
await app.AddStaffToOrganisationAsync(
OrganisationId,
new Staff()
{
Username = this.UserName
});
return RedirectToPage("/admin/companies/editor", new { id = OrganisationId.ToString() });
}
}
return Page();
}
AND
[BindProperty]
[Required]
public string UserName { get; set; }
public Guid OrganisationId { get; set; }
Changes to
[BindProperty]
[Required]
public string UserName { get; set; }
[BindProperty]
public Guid OrganisationId { get; set; }
Using the above:
On a Post that fails Model validation, the properties persist on the UI after
return Page();
The Redirect to page works now that I have move the parameter to the second argument as an anonymous object, instead of concatenating a string as a query parameter.
RedirectToPage("/admin/companies/editor", new { id = OrganisationId.ToString() }) is correct
If you need to pass a list as parameters for RedirectToPage you can do it like this:
return RedirectToPage("./Index", new { MigrationStatus = new List<int>() { (int)MigrationStatus.AwaitingStart, (int)MigrationStatus.InProgress } });
It will create a URL like this:
https://localhost:123/Migrations?MigrationStatus=3&MigrationStatus=4

MVC, Razor - Submitting form (inside #if) gives empty object in post

I am trying to save some changes to a database.
The view gets a Model, and should be able to make changes and have them saved. However, when i submit the form, the ContactInfo parameter in the post method is not null, but all the properties are.
I'm using MVC5 with Razor. the script is in a seperate .ts-file
In controller:
[HttpPost]
public bool SaveInfoChanges(ContactInfo editedContactInfo)
{
// Save data
return true;
}
Script:
export function saveInfoChanges() {
$("#EditContactInfoForm").submit();
};
HTML:
#if (true)
{
#using (Html.BeginForm("SaveInfoChanges", "ContactInfoBox", FormMethod.Post, new { id = "EditContactInfoForm" }))
}
<table>
#Html.HiddenFor(model => model.Name)
#Html.HiddenFor(model => model.Address1)
#Html.HiddenFor(model => model.Address2)
#Html.HiddenFor(model => model.ZipAndCity)
#Html.HiddenFor(model => model.Country)
<tr>
<td>test</td>
<td>
#Html.TextBoxFor(model => model.Address3, new { id = "ContactTypeInput", placeholder = "", style = "width:300px" })
</td>
</tr>
</table>
}
}
Model:
public class ContactInfo
{
public string ContactInfoBoxHeaderText { get; set; }
public string Name { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string ZipAndCity { get; set; }
public string Country { get; set; }
}
I can't think of more relevant information to give, but please don't hesitate to ask.
Thanks in advance!
Removing the #if solved the problem. I just need to find another way to hide/show the elements.

MVC Radio Button List Grid

I've got an issue trying to read the selected values from the radio button list in my [HttpPost] Controller. Any help would be greatly appreciated.
My models are the following:
public partial class RoleModel
{
public Guid RoleId { get; set; }
public string Description { get; set; }
public List<RoleModuleAccessRight> RoleModuleAccessRights { get; set; }
}
public class RoleModuleAccessRight
{
public string ModuleName { get; set; }
public int ModuleId { get; set; }
public bool HasFullControl { get; set; }
public bool HasReadOnly { get; set; }
public bool HasNoAccess { get; set; }
}
My Controllers:
[HttpGet]
public ActionResult Edit(Guid id)
{
RoleModel role = BusinessLayer.UserManager.GetRoleModel(id);
role.RoleModuleAccessRights = BusinessLayer.UserManager.GetModulesForRoleId(role.RoleId);
return PartialView("_Edit", role);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(RoleModel model)
{
if (ModelState.IsValid)
{
BusinessLayer.UserManager.UpdateAccessRights(model);
string url = Url.Action("List", "Roles");
return Json(new { success = true, url = url });
}
return PartialView("_Edit", model);
}
My View code:
#foreach (RoleModuleAccessRight item in Model.RoleModuleAccessRights)
{
#Html.HiddenFor(model => item.ModuleId)
string radioName = string.Format("RoleModuleAccessRights_{0}", item.ModuleId.ToString());
<tr>
<td>#item.ModuleName</td>
<td>
<div class="checkbox">
#Html.RadioButton(radioName, item.HasFullControl, item.HasFullControl)
</div>
</td>
<td>
<div class="checkbox">
#Html.RadioButton(radioName, item.HasReadOnly, item.HasReadOnly)
</div>
</td>
<td>
<div class="checkbox">
#Html.RadioButton(radioName, item.HasNoAccess, item.HasNoAccess)
</div>
</td>
</tr>
}
The issue im having is that when i post the form im not able to grab the information in my [HttpPost] Controller. It returns "null"
The mark up generated is the following:
Looking at your code, your ids are not unique which is going to break things, also using a dedicated template will simplify your problem. See this example https://stackoverflow.com/a/7668325
Another more generic article: Multiple radio button groups in MVC 4 Razor