Insert ForEach Value in View into the Database MVC - asp.net-core

Hi currently I am doing a shopping cart for my project
I would like to ask how can I import the values in a ForEach to the database.
For example, I have the following data in my view.
#foreach (Cart_Has_Services c in Model)
{
<div class="cart-row">
<div class="cart-items">#c.Cart_Service</div>
<div class="cart-items">#c.Additional_Notes</div>
<div class="cart-items">#c.Unit_Price</div>
<div class="cart-items">
<form asp-controller="Cart" asp-action="UpdateCart" formaction="post">
<input type="number" class="item-quantity-input" value="#c.Quantity" />
<input type="submit" class="btn btn-secondary" value="Update" />
</form>
</div>
<div class="cart-items">
<a asp-controller="Cart"
asp-action="DeleteItem"
asp-route-id="#c.Cart_Id"
onclick="return confirm('Delete Serivce #c.Cart_Service')">
Delete
</a>
</div>
</div>
}
As for now, I want to INSERT data (Cart Service, Additional Notes and Quantity) into my database (Order).
In my controller:
public IActionResult Checkout(Cart_Has_Services cart)
{
List<Cart_Has_Services> carts = DBUtl.GetList<Cart_Has_Services>("SELECT * FROM Cart");
string sql = #"INSERT INTO [Order](Order_Name,Order_Description,Order_Quantity)
VALUES('{0}','{1}',{2})";
int ord = DBUtl.ExecSQL(sql, cart.Cart_Service, cart.Additional_Notes, cart.Quantity);
if (ord == 1)
{
TempData["Message"] = "Perofrmance Successfully Created";
TempData["MsgType"] = "success";
return RedirectToAction("Success");
}
else
{
ViewData["Message"] = DBUtl.DB_Message;
ViewData["MsgType"] = "danger";
return View("ShoppingCart");
}
}
I tried the method that I have inserted but it created without inserting the data.
How can I solve this problem?
Hope can get some guidance.
Thank you

The form in the view only submit Quantity, without Cart_Service and Additional_Notes.
To submit their value, you may set hidden inputs in the form. Also you should set name attribute for the input for model binding.
#foreach (Cart_Has_Services c in Model)
{
<div class="cart-row">
<div class="cart-items">#c.Cart_Service</div>
<div class="cart-items">#c.Additional_Notes</div>
<div class="cart-items">#c.Unit_Price</div>
<div class="cart-items">
<form asp-controller="Cart" asp-action="UpdateCart" formaction="post">
<input type="hidden" name="Cart_Service" value="#c.Cart_Service" />
<input type="hidden" name="Additional_Notes" value="#c.Additional_Notes" />
<input type="number" name="Quantity" class="item-quantity-input" value="#c.Quantity" />
<input type="submit" class="btn btn-secondary" value="Update" />
</form>
</div>
<div class="cart-items">
<a asp-controller="Cart"
asp-action="DeleteItem"
asp-route-id="#c.Cart_Id"
onclick="return confirm('Delete Serivce #c.Cart_Service')">
Delete
</a>
</div>
</div>
}

Related

ASP .Net MVC Core binds wrong value in model list

I have a model with the following
- ModelData: List<ModelData>
With ModelData has the following:
- Name (string)
- LanguageId: Guid
And ViewBag has the following:
- Languages: IEnumerable<Microsoft.AspNetCore.Mvc.Rendering.SelectListItem>
And the view has the following:
#for (int i = 0; i < Model.ModelData.Count; i++)
{
<div class="row">
<div class="form-group">
<label asp-for="ModelData[i].LanguageId" class="control-label"></label>
<select asp-for="ModelData[i].LanguageId" asp-items="#ViewBag.Languages" class="form-control">
</select>
<span asp-validation-for="ModelData[i].LanguageId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ModelData[i].Name" class="control-label"></label>
<input asp-for="ModelData[i].Name" class="form-control" />
<span asp-validation-for="ModelData[i].Name" class="text-danger"></span>
</div>
#if (i > 0)
{
<div class="col-md-4">
<div class="form-group">
<button name="RemoveDataItem" value="#i.ToString()" class="btn btn-primary">Remove</button>
</div>
</div>
}
</div>
}
<input type="submit" name="AddDataItem" value="Add Item" class="btn btn-primary" />
<input type="submit" value="Create" class="btn btn-primary" />
And the controller as the following:
public async Task<IActionResult> CreateAsync(CreateModel model, string addDataItem, string removeDataItem)
{
if (addDataItem != null)
{
await FillViewBag();
model.ModelData.Add(new ModelData());
return View(model);
}
else if (removeDataItem != null)
{
await FillViewBag();
int itemIndex = int.Parse(removeDataItem);
model.ModelData.RemoveAt(itemIndex);
return View(model);
}
if (!ModelState.IsValid)
{
await FillViewBag();
return View(model);
}
// Save
}
And it works great, however I have a problem as the following:
Let's say i pressed the add button two times so now I have three records on ModelData and I entered a value in all textboxes and selected values in all select list, then I pressed remove next to the second row, so it goes to the controller action, the method removes the data of the correct index, and returns to the view, so Now I should find two rows, first with the data that was entered in the first row, and second with the data that was entered in the third row (because the second row is removed), however, what actually happens is that I end up with the data of the first two rows not the first and the third.
Any help is appreciated considering I did the following:
I validated that the item that is removed is the corect one (the second item), but the value is not bound correctly.
I added this attribute to the textbox value="#Model.ModelData[i].Name", and it worked correctly but I think this is not the correct way to solve this issue, also I did not find a similar attribute for the select tag.
Edit:
I also managed to add static Id for the input fields of each row, but it didn't help
Edit:
The problem is that the index is changed after the second row is removed, so the index of the third row (originally was 2) became 1 after removing the second row, and thus it now has the previous name attribute of second row "ModelData[1].Name" and not "ModelData[2].Name" and I think this is the problem which makes the browser keeps the previous value of the second row
For anyone who is concerned, I found the solution to this issue which is to add the following line before returning the view:
ModelState.Clear();
add index value to each item:
<input type="hidden" name="ModelData.index" value="#i">
update your view code like this:
#for (int i = 0; i < Model.ModelData.Count; i++)
{
<div class="row">
<input type="hidden" name="ModelData.index" value="#i">
<div class="form-group">
<label asp-for="ModelData[i].LanguageId" class="control-label"></label>
<select asp-for="ModelData[i].LanguageId" asp-items="#ViewBag.Languages" class="form-control">
</select>
<span asp-validation-for="ModelData[i].LanguageId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ModelData[i].Name" class="control-label"></label>
<input asp-for="ModelData[i].Name" class="form-control" />
<span asp-validation-for="ModelData[i].Name" class="text-danger"></span>
</div>
#if (i > 0)
{
<div class="col-md-4">
<div class="form-group">
<button name="RemoveDataItem" value="#i.ToString()" class="btn btn-primary">Remove</button>
</div>
</div>
}
</div>
}
Where's your source data? What you have done is just change the model parmeter.
Also, I can reproduce your problem, when I directly return View after remove the specify record. It can be fixed by using RedirectToAction
I made a demo based on your codes, you can refer to the below codes:
Controller:
public static CreateModel createModel = new CreateModel
{
ModelDatas = new List<ModelData>
{
new ModelData{ LanguageId = 1, Name = "a"},
new ModelData{ LanguageId = 2, Name = "b"},
new ModelData{ LanguageId = 3, Name = "c"}
}
};
public IActionResult Create()
{
FillViewBag();
return View(createModel);
}
[HttpPost]
public IActionResult Create(CreateModel model, string addDataItem, string removeDataItem)
{
if (addDataItem != null)
{
FillViewBag();
createModel.ModelDatas.Add(new ModelData());
return RedirectToAction("Create");
}
else if (removeDataItem != null)
{
FillViewBag();
int itemIndex = int.Parse(removeDataItem);
createModel.ModelDatas.RemoveAt(itemIndex);
return RedirectToAction("Create");
}
if (!ModelState.IsValid)
{
FillViewBag();
return RedirectToAction("Create");
}
return View();
}
View:
#model CreateModel
<form asp-action="Create" asp-controller="Test" method="post">
#for (int i = 0; i < Model.ModelDatas.Count; i++)
{
<div class="row">
<div class="form-group">
<label asp-for="ModelDatas[i].LanguageId" class="control-label"></label>
<select asp-for="ModelDatas[i].LanguageId" asp-items="#ViewBag.Languages" class="form-control">
</select>
<span asp-validation-for="ModelDatas[i].LanguageId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ModelDatas[i].Name" class="control-label"></label>
<input asp-for="ModelDatas[i].Name" class="form-control" />
<span asp-validation-for="ModelDatas[i].Name" class="text-danger"></span>
</div>
#if (i >= 0)
{
<div class="col-md-4">
<div class="form-group">
<button name="RemoveDataItem" value="#i.ToString()" class="btn btn-primary">Remove</button>
</div>
</div>
}
</div>
}
<input type="submit" name="AddDataItem" value="Add Item" class="btn btn-primary" />
<input type="submit" value="Create" class="btn btn-primary" />
</form>
Result:

Generating a valid __RequestVerificationToken from C#

One of the most popular books on ASP.NET Core is "Pro ASP.NET Core 3" by Adam Freeman.
In chapters 7-11, he builds an example application, SportsStore.
As you can see, each product in the listing gets its own 'Add To Cart' button:
If we do 'view source' on this page, we'll see the following HTML for that item in the product list:
<div class="card card-outline-primary m-1 p-1">
<div class="bg-faded p-1">
<h4>
Kayak
<span class="badge badge-pill badge-primary" style="float:right">
<small>$275.00</small>
</span>
</h4>
</div>
<form id="1" method="post" action="/Cart">
<input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="1" />
<input type="hidden" name="returnUrl" value="/" />
<span class="card-text p-1">
A boat for one person
<button type="submit" class="btn btn-success btn-sm pull-right" style="float:right">
Add To Cart
</button>
</span>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8KKqNOS0gwdMvC0-bdjTwWlvCcBJldeidwIX5b2f24gYblS9X1sqCwJWIEsKKOSf8kut0SQsQRLF3R1XBSYZkPGnta9YzRK4tcQl8dq_0uWmjeUhm8yMe90fWDt_x0smmAD1lmb9-BxQF8y_7-IQSz4" /></form>
</div>
Note the input tag towards the bottom:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8KKqNOS0gwdMvC0-bdjTwWlvCcBJldeidwIX5b2f24gYblS9X1sqCwJWIEsKKOSf8kut0SQsQRLF3R1XBSYZkPGnta9YzRK4tcQl8dq_0uWmjeUhm8yMe90fWDt_x0smmAD1lmb9-BxQF8y_7-IQSz4" />
If we look at the Views\Shared\ProductSummary.cshtml file in the SportsStore project, we'll see the code that is involved with generating these listing items:
#model Product
<div class="card card-outline-primary m-1 p-1">
<div class="bg-faded p-1">
<h4>
#Model.Name
<span class="badge badge-pill badge-primary" style="float:right">
<small>#Model.Price.ToString("c")</small>
</span>
</h4>
</div>
<form id="#Model.ID" asp-page="/Cart" method="post">
<input type="hidden" asp-for="ID" />
<input type="hidden" name="returnUrl" value="#ViewContext.HttpContext.Request.PathAndQuery()" />
<span class="card-text p-1">
#Model.Description
<button type="submit" class="btn btn-success btn-sm pull-right" style="float:right">
Add To Cart
</button>
</span>
</form>
</div>
As you can see, the form element in this case doesn't have an explicit inclusion of the input tag with the __RequestVerificationToken value. This form thus appears to be a tag helper which takes care of generting the input tag with the __RequestVerificationToken token.
As an experiment, let's suppose I have added the following method to Controllers\HomeController:
[HttpGet]
public ContentResult ButtonExample()
{
var token = "...";
return new ContentResult()
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content =
String.Format(
#"<!DOCTYPE html>
<html>
<body>
<form id=""1"" method=""post"" action=""/Cart"">
<input type=""hidden"" data-val=""true"" id=""ID"" name=""ID"" value=""1"" />
<button type=""submit"">Add to Cart</button>
</form>
<input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"" />
</body>
</html>",
token)
};
}
As you can see, this generates a very simple page with a single button which is intended to add the product with ID value 1 (i.e. the Kayak) to the cart.
I of course need to pass an appropriate value for the __RequestVerificationToken.
My question is, is there a way to get this value from C# so that I can include it in the method above?
The idea as shown above would be to set the token value here:
var token = "...";
This is then interpolated into the string that generates the HTML using String.Format.
UPDATE
This page mentions the following:
To generate the anti-XSRF tokens, call the #Html.AntiForgeryToken method from an MVC view or #AntiForgery.GetHtml() from a Razor page.
So I guess the question is, how do we do the equivalent from C# directly instead of from an MVC view or Razor page?
You can add the below code to your form which will generate the __RequestVerificationToken. It is used to prevent CSRF attacks Prevent XSRF/CSRF attacks.
<form action="/" method="post">
#Html.AntiForgeryToken()
</form>

Checkbox list values empty when ModelState is not valid and ASP.NET Core does return Page()

I created an ASP.NET Core 3.1 project. I have a form in it with several checkbox lists. I can get the values into the properties in POST and they are correctly filled in (e.g. List SelectedItems). However for some custom fields I have to do a validation in OnPost() method and if the conditions are not met or a ModelState is not valid, it return Page(). Normally I would expect that every property that was filled in in the form is still filled in, but the checkboxes are always empty and not a single one is checked. The other data (radio buttons, textboxes, etc.) are still filled in.
I even tried to put the values within the Razor page, but even then neither of the checkboxes was checked.
Here is an example of one of the checkboxes:
In Razor page:
#for (var i = 1; i <= 10; i++){
<input name="AreChecked" type="checkbox" id="#i" value="#i" /> #i<br />
<input type="hidden" value="true" id="#i" name="AreChecked" />}
Behind code:
[BindProperties]
public class TestFormModel : PageModel
{
[BindProperty]
public List<int> AreChecked { get; set; }}
public IActionResult OnPost()
{
//some other form check statements here
//...
if (ModelState.IsValid)
{
//process data code...
}
return Page();
}
Can someone help me with this?
You could use JQuery to achieve as shown :
#page
#model RazorPages3_1.AddimgModelModel
<div class="row">
<div class="col-md-4">
<form enctype="multipart/form-data" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Petimg.PetName" class="control-label"></label>
<input asp-for="Petimg.PetName" class="form-control" />
<span asp-validation-for="Petimg.PetName" class="text-danger"></span>
</div>
<div class="form-group">
<input asp-for="Uploads" class="form-control" />
</div>
#for (var i = 1; i <= 10; i++)
{
<input name="AreChecked" type="checkbox" id="#i" value="#i" /> #i<br />
<input type="hidden" value="true" id="#i" name=""AreChecked" />
}
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
#section Scripts {
<script>
var checkedlist = #Html.Raw(Json.Serialize(Model.AreChecked));;
if (checkedlist.length > 0) {
$.each(checkedlist, function (index, value) {
$('input[type=checkbox]').each(function () {
var id = $(this).attr("id");
if (id == value) {
$(this).attr('checked', 'checked');
}
})
});
}
</script>
}
Result

Insert Data using models with ASP.NET Core and EF Core 1.1

I have question in regarding with my code,
My primary goal here is to insert data which is from a model which in that model I set multiple models to grab data. Here is my code in my Model:
public class ModelLoan
{
public SelectList employees { get; set; }
public SelectList collectors { get; set; }
public SelectList loanTypes { get; set; }
public SelectList loanStatus { get; set; }
public SelectList loanFrequency { get; set; }
public LoanContract loanContract { get; set; }
}
Controller:
public IActionResult Index()
{
//For Employees List
var lstEmployee = _Context.Employee.OrderBy(e => e.Fname + ' ' + e.Lname).Select(x => new { Id = x.EmployeeId, Value = x.Fname + ' ' + x.Lname });
//For Collectors List
var lstCollector = _Context.Collectors.OrderBy(c => c.CollectorName).Select(x => new { Id = x.CollectorId, Value = x.CollectorName});
//For Loan Type List
var lstLoanType = _Context.LoanType.OrderBy(lt => lt.Description).Select(x => new { Id = x.LoanTypeId, Value = x.Description });
//For Loan Status List
var lstLoanStatus = _Context.LoanStatus.OrderBy(ls => ls.StatusName).Select(x => new { Id = x.LoanStatusId, Value = x.StatusName });
//For Loan Frequency List
var lstLoanFrequency = _Context.LoanFrequency.OrderBy(lf => lf.LoanFrequencyId).Select(x => new { Id = x.LoanFrequencyId, Value = x.Description });
var myList = new ModelLoan();
myList.employees = new SelectList(lstEmployee, "Id", "Value");
myList.collectors = new SelectList(lstCollector, "Id", "Value");
myList.loanTypes = new SelectList(lstLoanType, "Id", "Value");
myList.loanStatus = new SelectList(lstLoanStatus, "Id", "Value", 1003);
myList.loanFrequency = new SelectList(lstLoanFrequency, "Id", "Value", 2);
//Set Default
return View(myList);
}
Now from this I put those data on a SelectList method which it is easy for me to view through using <select> tag. But the problem is I don't know how to extract the value of it.
Now here is my View:
#model prjPEMCOLoan.Models.ModelLoan
<form asp-controller="Loan" asp-action="SaveLoanContract" role="form" method="post" style="padding-top: 2em;">
<h2>Loan Application</h2>
<div class="form-group">
<label>Select Employee</label>
<select class="form-control" name="lc.loanContract.EmployeeId" asp-items="#Model.employees"></select>
<small class="form-text text-muted">Select the specific employee. If employee name is not on the list then you register the employee <a asp-controller="Employee" asp-action="Index">here</a>.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label>Select Collector</label>
<select class="form-control" name="lc.loanContract.CollectorId" asp-items="#Model.collectors"></select>
<small class="form-text text-muted">Collector will be the one to check your loan status in this system. He/She will be the responsible for accepting/rejecting your loan applied.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label>Select Loan Type</label>
<select class="form-control" name="lc.loanContract.LoanTypeId" asp-items="#Model.loanTypes"></select>
<small class="form-text text-muted">Choose your desired loan type you want to apply.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label>Status</label>
<select class="form-control" name="lc.loanContract.LoanStatusId" asp-items="#Model.loanStatus" disabled></select>
<small class="form-text text-muted">Upon registration to this loan you are set to pending status as default to check your loan by the collectors.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label>Status</label>
<select class="form-control" name="lc.loanContract.LoanFrequencyId" asp-items="#Model.loanFrequency" disabled></select>
<small class="form-text text-muted">Upon registration to this loan you are set to a <b>Biweekly</b> because your payroll mostly computed and deducted every Biweekly day of the month.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-inline">
<div class="form-group">
<label> Date Applied: </label><span> </span>
<input disabled type="date" name="lc.loanContract.LoanDateStart" id="dpDateNow" class="form-control" /><br />
<small class="form-text text-muted">This is the date you applied.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label> Date Approved: </label><span> </span>
<input type="date" id="dpDateApproved" name="lc.loanContract.LoanDateEnd" class="form-control" /><br />
<small class="form-text text-muted">This is the date where your loan will be approved.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label> Due Date: </label>
<input type="date" id="dpDateDue" name="lc.loanContract.LoanPaymentDue" class="form-control" /><br />
<small class="form-text text-muted">Date where you expected to pay them all.</small>
<span asp-validation-summary="All"></span>
</div>
</div><br />
<div class="form-group">
<label>Loan Amount</label>
<input required placeholder="Enter Amount (must be number)" name="lc.loanContract.LoanAmount" class="form-control" type="number"/>
<small class="form-text text-muted">Input the amount you want to loan.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-control" style="height: 100%">
<label>Terms and Condition</label><br />
<input required type="checkbox" name="lc.loanContract.isAcceptTac"/>
<small class="form-text text-muted"><b>I Agree, Read and Accept the terms terms and condition. </b><br />
PEMCO Loan is an online peer to peer loans community that connects people with money to lend to people who need to borrow money. Lending Hub works in a controlled and regulated environment that allows members to interact anonymously (from each other). To make sure that a secure and safe community is maintained we have developed these terms and conditions of use to assist all members. These terms and conditions explain what you can expect from us, what we expect from you, and what members can expect from each other and these terms and conditions will apply to you whenever you browse, transact or interact on the site.
</small>
<span asp-validation-summary="All"></span>
</div><br />
<div class="form-control-static">
<button type="submit" class="btn btn-primary"><b>Apply</b></button>
</div>
</form>
Now everything is on set on the view the only thing I have problem right now is inserting data from the ModelLoan.loanContract which I defined here in my code:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveLoanContract(ModelLoan lc)
{
if (ModelState.IsValid)
{
await _Context.LoanContract.AddAsync(lc.loanContract);
await _Context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(lc.loanContract);
}
What suppose to be the problem on my code? I miss a little thing but I don't know where. Can you guys help me with this? Thanks!
Update:
This is exception happens when I the asp-action = "SaveLoanContract" since it is associated with the index itself.
InvalidOperationException: The view 'SaveLoanContract' was not found. The following locations were searched:
/Views/Loan/SaveLoanContract.cshtml
/Views/Shared/SaveLoanContract.cshtml
However when I changed the asp-action = "Index"
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'PEMCOLoan.DAL.Entities.Models.LoanContract', but this ViewDataDictionary instance requires a model item of type 'prjPEMCOLoan.Models.ModelLoan'.
Let me know if I missed something.
With ASP.NET Core you have the new tag asp-for. Change your view to:
#model prjPEMCOLoan.Models.ModelLoan
<form asp-controller="Loan" asp-action="SaveLoanContract" role="form" method="post" style="padding-top: 2em;">
<h2>Loan Application</h2>
<div class="form-group">
<label>Select Employee</label>
<select class="form-control" asp-for="loanContract.EmployeeId" asp-items="#Model.employees"></select>
<small class="form-text text-muted">Select the specific employee. If employee name is not on the list then you register the employee <a asp-controller="Employee" asp-action="Index">here</a>.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label>Select Collector</label>
<select class="form-control" asp-for="loanContract.CollectorId" asp-items="#Model.collectors"></select>
<small class="form-text text-muted">Collector will be the one to check your loan status in this system. He/She will be the responsible for accepting/rejecting your loan applied.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label>Select Loan Type</label>
<select class="form-control" asp-for="loanContract.LoanTypeId" asp-items="#Model.loanTypes"></select>
<small class="form-text text-muted">Choose your desired loan type you want to apply.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label>Status</label>
<select class="form-control" asp-for="loanContract.LoanStatusId" asp-items="#Model.loanStatus" disabled></select>
<small class="form-text text-muted">Upon registration to this loan you are set to pending status as default to check your loan by the collectors.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label>Status</label>
<select class="form-control" asp-for="loanContract.LoanFrequencyId" asp-items="#Model.loanFrequency" disabled></select>
<small class="form-text text-muted">Upon registration to this loan you are set to a <b>Biweekly</b> because your payroll mostly computed and deducted every Biweekly day of the month.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-inline">
<div class="form-group">
<label> Date Applied: </label><span> </span>
<input disabled type="date" asp-for="loanContract.LoanDateStart" id="dpDateNow" class="form-control" /><br />
<small class="form-text text-muted">This is the date you applied.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label> Date Approved: </label><span> </span>
<input type="date" id="dpDateApproved" asp-for="loanContract.LoanDateEnd" class="form-control" /><br />
<small class="form-text text-muted">This is the date where your loan will be approved.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-group">
<label> Due Date: </label>
<input type="date" id="dpDateDue" asp-for="loanContract.LoanPaymentDue" class="form-control" /><br />
<small class="form-text text-muted">Date where you expected to pay them all.</small>
<span asp-validation-summary="All"></span>
</div>
</div><br />
<div class="form-group">
<label>Loan Amount</label>
<input required placeholder="Enter Amount (must be number)" asp-for="loanContract.LoanAmount" class="form-control" type="number"/>
<small class="form-text text-muted">Input the amount you want to loan.</small>
<span asp-validation-summary="All"></span>
</div>
<div class="form-control" style="height: 100%">
<label>Terms and Condition</label><br />
<input required type="checkbox" asp-for="loanContract.isAcceptTac"/>
<small class="form-text text-muted"><b>I Agree, Read and Accept the terms terms and condition. </b><br />
PEMCO Loan is an online peer to peer loans community that connects people with money to lend to people who need to borrow money. Lending Hub works in a controlled and regulated environment that allows members to interact anonymously (from each other). To make sure that a secure and safe community is maintained we have developed these terms and conditions of use to assist all members. These terms and conditions explain what you can expect from us, what we expect from you, and what members can expect from each other and these terms and conditions will apply to you whenever you browse, transact or interact on the site.
</small>
<span asp-validation-summary="All"></span>
</div><br />
<div class="form-control-static">
<button type="submit" class="btn btn-primary"><b>Apply</b></button>
</div>
Your controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveLoanContract(ModelLoan lc)
{
if (ModelState.IsValid)
{
await _Context.LoanContract.AddAsync(lc.loanContract);
await _Context.SaveChangesAsync();
return RedirectToAction("Index");
}
// Get the list data again and populate the entire model
lc.employees = new SelectList(lstEmployee, "Id", "Value");
lc.collectors = new SelectList(lstCollector, "Id", "Value");
// and so on
return View("Index", lc); // also it may be required to specify the full path for the view like ~/Views/Home/Index.cshtml
}
PS: This <span asp-validation-summary="All"></span> part of the code does not require to be on each input. You can place once before the inputs.

Why is the Bind attribute seemingly breaking my model binding of nested objects?

Could someone help me resolve this issue. I'm trying to limit over posting with bind param action but it seems that it doesn't work at all. When I removed the Bind keyword, everything started to work as a charm.
Here is the code sample:
View Model:
public class ProductCreateViewModel
{
public Product Product { get; set; }
public ICollection<IFormFile> Images { get; set; }
}
Action:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Product.Id,Product.CategoryId,Product.Description,Product.Title")] ProductCreateViewModel productVM)
{
if (ModelState.IsValid)
{
_context.Add(productVM.Product);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewData["CategoryId"] = new SelectList(_context.Categories.Include(c => c.Categories).Where(c => c.ParentCategoryId == null), "Id", "Name", productVM.Product.CategoryId);
return View(productVM);
}
View:
#model CatalogWebApp.Models.ProductsViewModels.ProductCreateViewModel
#{
ViewData["Title"] = "Add Product";
ViewData["BigPageTitle"] = "Products";
ViewData["PageBoxTitle"] = "Add New Product";
}
<form asp-action="Create">
<div class="form-horizontal">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Product.CategoryId" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select name="Product.CategoryId" class ="form-control">
#foreach(Category item in (ViewBag.CategoryId as SelectList).Items)
{
<option value="#item.Id">#item.Name</option>
if (item.Categories != null && item.Categories.Count > 0)
{
foreach (var subCat in item.Categories)
{
<option value="#subCat.Id">--#subCat.Name</option>
}
}
}
</select>
</div>
</div>
<div class="form-group">
<label asp-for="Product.Description" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Product.Description" class="form-control" />
<span asp-validation-for="Product.Description" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Product.Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Product.Title" class="form-control" />
<span asp-validation-for="Product.Title" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Could someone pelase indicate if I have a problem or it is only a known asp.net core issue?
I'm not quite sure why you using Bind for your case.
Just create sepatate ViewModel with only properties you need like ProductCreateStort.
Then use this ViewModel in your controller signature and inherit your main model from it.
This way you won't mess with Bind and limit your params on POST
While I'm fairly new to ASP.NET Core myself (and coming to this question 7 months late), I ran into this same issue. I think the key here is that you have to bind "Product" for it to be considered. Binding "Product.Id" by itself doesn't appear to be good enough. So this should work:
[Bind("Product,Product.Id,Product.CategoryId,Product.Description,Product.Title")]
Of course, Hamid Mosalla's comment is a better option if ALL of your bound properties are on the nested object (which leads to wonder why you need a view model in the first place). In my case, I have a nested object AND a local property, so using the "Prefix" solution wasn't the right thing to do.
Anyway, hope this helps someone.
You need to pass your values as params string[], not as a single string separated by commas:
[Bind("Product.Id","Product.CategoryId","Product.Description","Product.Title")]
See Source