I am trying to make an MVC 5 Razor web page with a table within it, Every row in this table contains a Delete button which is post action:
<table>
<thead>
<tr>
<th>Name</th>
.
.
.
<th>Actions</th>
</tr>
</thead>
<tbody>
#foreach(var item in Model)
{
<tr>
<td>#item.Name</td>
.
.
.
<td>
#using(Html.BeginForm("Delete", "Person", FormMethod.Post))
{
#html.AntiForgetyToken()
#html.Hidden("personId", item.PersonId)
<button type="submit" class="btn">Delete</button>
}
</td>
</tr>
}
</tbody>
</table>
And in the controller:
public class PersonController : BaseController
{
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Delete(int personId)
{
// Do Something...
}
}
Now my problem is when pressing Delete button in any row of the table it always submitting the first form in the page which means the first person in the table is always being deleted no matter what submit button i press.
Any ideas to solve this issue?
Edit:
The generated page html
<div class="page-header">
<h2>Person List</h2>
</div>
<table class="row table table-striped">
<thead>
<tr class="text-primary">
<th class="text-center">#</th>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center">5</td>
<td>Ismail</td>
<td>ismail#example.com</td>
<td>Active</td>
<td>
<form action="/WebApp/Person/Delete?PersonID=5" method="post">
<input name="__RequestVerificationToken" type="hidden" value="Uxhp0Bq1ATAwOXNODHmc74f12O2-dvFhQ5kletbmDkq64CEPWZlXXPKuHDoqSy4DXF6mJhYfGffc_YAn1yERxp69JCUT9IlGTKdfirvVvqE1" />
<div class="text-center">
<div class="btn-group btn-group-xs">
<a class="btn btn-default" href="/WebApp/Person/Details?PersonID=5">View</a>
<a class="btn btn-default" href="/WebApp/Person/Edit?PersonID=5">Edit</a>
<button class="btn btn-danger>Delete</button>
</div>
</div>
</form>
</td>
</tr>
<tr>
<td class="text-center">6</td>
<td>MoHaKa</td>
<td>MoHaKa#example.com</td>
<td>Active</td>
<td>
<form action="/WebApp/Person/Delete?PersonID=6" method="post">
<input name="__RequestVerificationToken" type="hidden" value="R4CUAuVpbGihZvrFxxCjCL_oJ7tgkS_Xxh67i_xCpMXpvZKR5ASUWrSCvjg52yRorF-Ypeau1oZwDi96caHyUj-gmBeHnx7NBgfJBLkLPnQ1" />
<div class="text-center">
<div class="btn-group btn-group-xs">
<a class="btn btn-default" href="/WebApp/Person/Details?PersonID=6">View</a>
<a class="btn btn-default" href="/WebApp/Person/Edit?PersonID=6">Edit</a>
<button class="btn btn-danger>Delete</button>
</div>
</div>
</form>
</td>
</tr>
</tbody>
</table>
Use this instead of your form code:
#using(Html.BeginForm("Delete", "Person", new { item.PersonId }))
{
#Html.AntiForgetyToken()
<button class="btn">Delete</button>
}
ASP.NET MVC default action behavior: primitive types are first looked in query string.
Maybe it's better to use just one form and add a cell like this instead of several forms:
<td onclick="window.location='#Url.Action("Delete", new { PersonID = item.PersonId })'" style="cursor:pointer;">
</td>
Related
I am trying to redirect to another local web page using a tag in Thymeleaf and Spring boot but it is
not working. I am redirecting from index.html to addEdit.html which are in the same folder.
Here is my code.
index.html
<div class="container">
<p th:text="${message}"></p>
<a th:href="#{/addEdit.html}" class="btn btn-outline-info">Add Employee</a> //not working
<table class="table table-bordered table-dark">
<thead class="">
<tr>
<th>#</th>
<th>Name</th>
<th>Departmen</th>
<th>Position</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="employee : ${employees}" >
<th th:text="${employee.id}"></th>
<td th:text="${employee.name}"></td>
<td th:text="${employee.department}"></td>
<td th:text="${employee.position}"></td>
<td>
<form action="delete">
<input type="submit" value="Delete" class="btn btn-outline-warning"/>
</form>
</td>
<td>
<form action="edit">
<input type="submit" value="Edit" class="btn btn-outline-info"/>
</form>
</td>
</tr>
</tbody>
</table>
</div>
my EmployeeController
#Autowired
private employeeRepo repo;
#RequestMapping("/")
public String home(Model model) {
List<Employee> list = new ArrayList<>();
list = repo.findAll();
model.addAttribute("employees",list);
return "index";
}
#PostMapping("/addEmployee")
public void addEmployee(Employee employee,Model model) {
repo.save(employee);
model.addAttribute("message","Add Successfully");
home(model);
}
my addEdit.html
<div class="container bg-light">
<form action="addEmployee">
<input class="form-control form-control-sm" type="text" placeholder="Name" name="name"><br>
<input class="form-control form-control-sm" type="text" placeholder="Department" name="department"><br>
<input class="form-control form-control-sm" type="text" placeholder="Position" name="postion"><br/>
<input type="submit" value="Add Employee" class="btn btn-outline-success btn-lg btn-block">
</form>
</div>
You should not include the .html in your link. The link should point to a URL that your controller exposes. There is currently no controller method that exposes the /addEdit url for example.
So update your controller:
#GetMapping
public String addEmployee(Model model) {
// add model attributes here as needed first
return "addEdit" // This refers to the view, so 'addEdit.html'
}
Now update the link to:
<a th:href="#{/addEdit}" class="btn btn-outline-info">Add Employee</a>
I want to expand and collapse table row using Bootstrap Accordion.
Currently, if I click on any row, it expands and collapse. But what I want is that, if I click on second row then first row should collapse if it is expanded then and so on.
<div class=" panel-body">
<table class="table">
#foreach (var item in Model)
{
<tr>
<td class="accordion-toggle" data-toggle="collapse" data-target="#AA_#(item.Id)">
<button class="bb" type="button">
#Html.Raw(item.H)
</button>
</td>
<td>
#Html.Raw(item.E)
</td>
</tr>
<tr>
<td id="AA_#(item.Id)" class="accordion-body collapse">
<table>
<tr>
<td>
#Html.Raw(item.D)
</td>
<td>
#Html.Raw(item.B)
</td>
</tr>
</table>
</td>
</tr>
}
</table>
</div>
I researched my problem on SO and found one solution provided by #tmg on here. Many thanks to #tmg. I followed the same in my scenario and it worked for me.
<div class="panel-body">
<table class="table">
#foreach (var item in Model)
{
<tr class="accordion-toggle" data-toggle="collapse" data-target="#AA_#(item.Id)">
<td>
<button class="bb" type="button">
#Html.Raw(item.H)
</button>
</td>
<td>
#Html.Raw(item.E)
</td>
</tr>
<tr>
<td class="hiddenRow">
<div class="accordian-body collapse" id="AA_#(item.Id)">
<table>
<tr>
<td>
#Html.Raw(item.D)
</td>
<td>
#Html.Raw(item.B)
</td>
</tr>
</table>
</div>
</td>
</tr>
}
</table>
</div>
And added JQuery to collapse and toggle table row
$('.table .accordian-body').on('show.bs.collapse', function () {
$(this).closest("table")
.find(".collapse.in")
.not(this)
.collapse('toggle')
})
Added Style for hiddenRow
.hiddenRow {
padding: 0 !important;
}
I am new to MVC. Not sure how to NOT have the page do a full blinking refresh on click of the submit button. Have not found examples on this other than to put the code in an action in my controller, and I would prefer to have the code as I have it below. Thank you for your assistance!
<form method="post">
<div style="float: left">
Enter the number of days previous to today for which to search: <input type="text" name="NumberOfDays" value="#Request.Form["numberOfDays"]"/>
</div>
<div class="puck-button">
<input type="submit" value="GO!" class="btn btn-primary"/>
</div>
<br/><br/><br/><br/>
#{
var numberOfDays = int.Parse(Request.Form["NumberOfDays"].IsNullOrWhiteSpace() ? "0" : Request.Form["NumberOfDays"]);
var startDate = DateTime.Today.AddDays(-numberOfDays);
<table id="puckBoard" class="gridView">
<thead>
<tr>
#foreach (var item in Model.ProcessSteps)
{
<th>
<div class="puck-step">
#Html.DisplayFor(modelItem => item.Description)
</div>
</th>
}
</tr>
</thead>
<tbody>
#if(startDate != DateTime.Today) { #CompactTableRows(startDate, Model.Modules) }
else
{ #GetSimpleModuleTableRow(Model.Modules) }
</tbody>
</table>
}
</form>
I need help creating an 'edit' action that selects a specific model from a list of models, and in that table, edits the model. A quick-edit of sorts.
<tbody>
{{#each}}
<tr class="people-list">
<td>
<input type="checkbox" class="toggle">
<label class="category-text">{{#linkTo 'category' this}}{{Name}}{{/linkTo}} </label>
<img class="table-img" src="images/x.png">
<img class="table-img" {{action 'edit'}} src="images/pencil-grey.png">
</td>
</tr>
{{/each}}
</tbody>
By clicking {{action 'edit'}}, {{Name}} becomes and editable input. This is all done without leaving the '/categories' url.
Thanks guys :D
<tbody>
{{#each}}
<tr class="people-list">
<td>
<input type="checkbox" class="toggle">
{{#if isEdit}}
{{input type="text" valueBinding="Name" name="Name"}}
{{else}}
<label class="category-text">{{#linkTo 'category' this}}{{Name}}{{/linkTo}}</label>
{{/if}}
<img class="table-img" src="images/x.png">
<img class="table-img" {{action 'edit'}} src="images/pencil-grey.png">
</td>
</tr>
{{/each}}
</tbody>
And then on the controller:
actions: {
edit:function(){
this.set('isEdit', true);
}
}
I have a view in 2 sections.
The top section I input fields and submit to save them.
In the second section I have an autocomplete textbox. I select an item in autocomplete, and when I click submit I want to add that item to a datatable.
So for the first part when I click submit I save the details via a HttpPost method on the controller.
For the second part I intend to save it via an Ajax call for the controller and then bring back a partial view with the results. I have not coded the partial view yet, that is next.
Now I am new to Ajax.BeginForm and I am struggling with it.
I was hoping that the submit button inside the Ajax.BeginForm would only apply to that part of the form.
But in fact it calls the HttpPost method for the whole form.
So how do I fix this?
My view looks like;
#using ITOF.HtmlHelpers
#model ITOF.Models.OngoingContractViewModel
#{
ViewBag.Title = "EditOngoingContractDetails";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("EditOngoingContractDetails", "Contract", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Contract.ContractId)
<h1>Edit Ongoing Contract Details</h1>
<fieldset>
<legend>#Model.Contract.Heading</legend>
<p>Where you see <span class="error">*</span> you must enter data.</p>
<table>
<tr>
<td style="text-align: right">
#Html.LabelFor(model => model.Contract.EndDate)
</td>
<td>
#Html.EditorFor(model => model.Contract.EndDate)
</td>
</tr>
<tr>
<td style="text-align: right">
#Html.LabelFor(model => model.Contract.Organogramme)
</td>
<td>
<input type="file" id="PDF" name="file" />
#Html.HiddenFor(model => model.Contract.Organogramme)
</td>
</tr>
#if (!string.IsNullOrWhiteSpace(Model.Contract.Organogramme))
{
<tr>
<td></td>
<td>
The current organogramme is <span class="HighlightTextRed">#Model.GetOrganogrammeName()</span>
for the contract <span class="HighlightTextRed">#Model.Contract.ContractName</span><br/>
Click here to see the last saved organogramme
</td>
</tr>
}
<tr>
<td style="text-align: right">
#Html.LabelFor(model => model.Contract.AssistantRLOManagerId)
</td>
<td>
#Html.DropDownListFor(model => model.Contract.AssistantRLOManagerId, Model.AssistantRloManagerSelectList, "--N/A--")
</td>
</tr>
#if (this.TempData["SuccessMessage"] != null)
{
<tr>
<td colspan="2" class="success">#this.TempData["SuccessMessage"].ToString()</td>
</tr>
}
<tr>
<td colspan="2" style="padding-top: 20px; text-align: center;"><input type="submit" value="Save" /></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>Add an existing Site to this contract: </legend>
#using (Ajax.BeginForm("AddExistingSite", new AjaxOptions { UpdateTargetId = "siteRows" }))
{
<input type="text" name="q" style="width: 800px"
data-autocomplete="#Url.Action("SiteSearch", "DataService", new { contractId = #Model.Contract.ContractId })" />
<input type="submit" value="Add site to contract" />
}
#if (Model.SiteList.Count > 0)
{
<table id="siteDataTable" class="display">
<thead>
<tr>
<th>Main Site?</th>
<th>Type</th>
<th>Address</th>
<th>Map</th>
<th>Telephone</th>
<th>Email</th>
</tr>
</thead>
<tbody id="siteRows">
#foreach (var item in Model.SiteList)
{
<tr id="#item.SiteContract.SiteContractId">
<td>#item.SiteContract.MainSiteFlag</td>
<td>#item.Site.SiteType</td>
<td>#item.Site.Address</td>
<td>#item.Site.MapUrl</td>
<td>#item.Site.Telephone</td>
<td>#item.Site.Email</td>
</tr>
}
</tbody>
</table>
<div class="add_delete_toolbar" />
}
#Html.ListLink("Back to List")
</fieldset>
}
Oh no, you just cannot nest HTML forms. That's not supported. You will have to rethink your design. This really has absolutely nothing to do with ASP.NET MVC and things like Html.BeginForm or Ajax.BeginForm. The HTML specification simply tells you that the <form> tag cannot be nested and if you nest it you will get undefined behavior that could vary between browsers.
For example you could implement the autocomplete functionality using jquery UI autocomplete plugin and get rid of the Ajax.BeginForm.