Can you please help out?
Controller "Create" POST method arguments is unable to capture the dynamically generated hidden fields.
After I click submit, the post method (if checked on the browser Network tab / developer tools) shows that the hidden fields are captured (see below image) but when I set a breakpoint on the Create POST method in the controller, the field are sometimes captured less than expected, sometimes not even captured.
The issue comes from when you click on "Remove" button, to remove the hidden field, then the problem start.
If you display the form and just fill that in then submit, everything is fine, all the hidden fields are captured and sent to the model after POST; only when you start deleting one or more hidden fields (from remove button), then it start behaving sometimes it captures the remain field, sometimes it shows 0... even though in the network tab it shows that it posted the correct number of hidden field.
PS : I just tried to delete the hidden input field manually, under Elements tab, dev tools, it has the same behavior as what I said above, I mean the network tab POST shows the correct number being posted, but the controller model capture incorrect number, sometimes nothing being captured.
Classes
public class Product
{
[Key]
public int ProductId { get; set; }
public int ProductsItemInStock { get; set; }
public decimal CostPerItem { get; set; }
public string ItemName { get; set; }
public IList<ProductInvoice> ProductInvoices { get; set; }
}
public class Invoice
{
[Key]
public int InvoiceId { get; set; }
public DateTime CreatedDate { get; set; }
public Guid CreateByUser { get; set; }
public int TotalInvoice { get; set; }
public IList<ProductInvoice> ProductInvoices { get; set; }
}
public class ProductInvoice
{
[Key]
public int ProductId { get; set; }
public Product Product { get; set; }
[Key]
public int InvoiceId { get; set; }
public Invoice Invoice { get; set; }
}
Controller
// GET: Invoices/Create
public IActionResult Create()
{
PopulateProductsDropDownList();
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("InvoiceId,CreatedDate,CreateByUser,TotalInvoice,ProductInvoices")] Invoice invoice)
{
if (ModelState.IsValid)
{
_context.Add(invoice);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(invoice);
}
private void PopulateProductsDropDownList()
{
IEnumerable<SelectListItem> items = _context.Products.Select(c => new SelectListItem
{
Value = c.ProductId.ToString() + "-" + c.CostPerItem,
Text = c.ItemName
});
ViewData["Products"] = items;
}
View
@model GeniiApp.Models.Invoice
@using System.Collections.Generic;
@{
ViewData["Title"] = "Create";
}
<head>
<script src="~/lib/jquery/dist/jquery.js"></script>
<link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
</head>
<h1>Create</h1>
<h4>Invoice</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="CreatedDate" class="control-label"></label>
<input asp-for="CreatedDate" class="form-control" />
<span asp-validation-for="CreatedDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreateByUser" class="control-label"></label>
<input asp-for="CreateByUser" class="form-control" />
<span asp-validation-for="CreateByUser" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Add items to invoice</label>
<div class="form-select form-select-lg mb-3">
@Html.DropDownList("SelectedProducts",
new SelectList((System.Collections.IEnumerable)ViewData["Products"], "Value", "Text", new { @class = "form-control dropdown-list" }))
<span class="span-add-item">
<button type="button" class="btn btn-info float-right" id="btn-add-item">Add</button>
</span>
</div>
</div>
<div class="form-group card-frame">
<div class="card" style="">
<div class="card-body">
<h5 class="card-title">Items added to invoice</h5>
<hr />
<span id="main-body-card"></span>
</div>
</div>
</div>
<div class="form-group frame-total-invoice">
<label asp-for="TotalInvoice" class="control-label"></label>
<input id="total-invoice-input" asp-for="TotalInvoice" class="form-control" readonly />
<span asp-validation-for="TotalInvoice" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
<script>
$(document).ready(function () {
var totalInvoice = 0;
var i = 0;
var p = 0;
$("#btn-add-item").click(function () {
var selectedItem = $('#SelectedProducts :selected').text();
var getCostPerItem = $('#SelectedProducts :selected').val();
var productId;
var itemToRemove;
productId = getCostPerItem.split("-")[0];
getCostPerItem = getCostPerItem.split("-")[1];
getCostPerItem = getCostPerItem.split(".")[0];
totalInvoice = parseInt(totalInvoice) + parseInt(getCostPerItem);
itemToRemove = "ProductId_" + productId;
itemToRemoveClass = "ProductId_" + productId + "_" + p + "_Class";
$('<div id="created_div" class="' + itemToRemoveClass + '">' + '<div>' + '<b>+</b> ' + selectedItem + ' : R ' + ' ' + getCostPerItem + '<button type="button" id="' + productId + '_' + p + '" class="btn btn-danger btn-sm btn-remove-item float-right">Remove</button></div><br></div>').insertAfter('#main-body-card');
p++;
$("<input class='" + productId + "' value='" + productId + "' name='ProductInvoices[" + i + "].ProductId' type='hidden' />").insertAfter('#created_div');
i++;
$("#total-invoice-input").val(totalInvoice);
});
// Moved inside to test the behaviour
$(document).on("click", "button", function (event) {
//console.log(event.target.id);
var el = event.target.id;
//alert(el);
var start = "ProductId_";
var end = "_Class";
var entire = start + el + end;
var id = el.split("_")[0];
//alert(id)
$("div." + entire).remove(); // delete the div
// Here is the problem !!!
// this line deletes the hidden field as well
// if commented everything works fine, I mean it captures the correct number of hidden field and pass it to the model/controller method
// if uncommented then the issue starts
$("input[type='hidden']").remove("." + id);
});
});
// Moved outside
//$(document).on("click", "a", function (event) {
// //console.log(event.target.id);
// var el = event.target.id;
// alert(el);
// var start = "ProductId_";
// var end = "_Class";
// var entire = start + el + end;
// var id = el.split("_")[0];
// alert(id)
// $("div." + entire).remove();
// $("input[type='hidden']").remove("." + id);
//});
</script>
}