How to submit related entities in asp.net core razor page form? - asp.net-core

I have two models Invoice and Product, one invoice can have many products as usual.
public class InvoiceModel
{
public string Name { get; set; }
public string Description { get; set; }
[Required(ErrorMessage = "Products are required!")]
public List<ProductModel> Products { get; set; }
}
public class ProductModel
{
public string Name { get; set; }
public string Model { get; set; }
public string Brand { get; set; }
}
and i want to create a new invoice with products.
so when i generate razor page for invoice create, it only generate input fields for Name and Description,
then i added product table, and button to the form, when the user clicks on add product button, a new row is added to the table using jsscript.
I have referred to this Url for binding complex object lists, but when I submit the form newly added products are not posted to the server-side.
Can anyone point me to the right way to resolve this issue,
Thanks

Try my codes bellow and my test result in screenshot.
#page
#model IndexModel
#{
}
<h3>#ViewData["confirmation"]</h3>
<form class="form-horizontal" method="post">
<div class="form-group">
<label for="Name" class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
<input asp-for="CC.Name" type="text" class="form-control">
</div>
</div>
<div class="form-group">
<label for="Description" class="col-sm-2 control-label">Description</label>
<div class="col-sm-10">
<input asp-for="CC.Description" type="text" class="form-control">
</div>
</div>
<script type="text/javascript">
var mytab = document.getElementById("myTable");
var i = 0;
function myfunction() {
document.getElementById("myTable").style.display = "block";
document.getElementById("myTable").insertRow(-1).innerHTML =
'<td> <input type="text" name="cc.Products[' +i + '].Name" /> </td><td> <input type="text" name="cc.Products[' + i + '].Model" /> </td><td> <input type="text" name="cc.Products[' + i + '].Brand" /> </td>';
i++;
}
</script>
<button type="button" onclick="myfunction()">Add Product</button>
<table id="myTable" style="display: none;" class="table table-striped">
<tr>
<th>Name</th>
<th>Model</th>
<th>Brand</th>
</tr>
</table>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Create</button>
</div>
</div>
</form>
enter image description here
enter image description here

According to your description ,I guess you used the wrong input tag's name this is the reason why you coulnd't get the ProductModel's vaule.
If your post method's parameter name like public InvoiceModel ProductModels { get; set; }, then the table's name should like this ProductModels.Products[0].Name and ProductModels.Products[1].Name.
More details, you could refer to below codes:
<form class="form-horizontal" method="post">
<div class="form-group">
<label for="Name" class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
<input asp-for="ProductModels.Name" type="text" class="form-control">
</div>
</div>
<div class="form-group">
<label for="Description" class="col-sm-2 control-label">Description</label>
<div class="col-sm-10">
<input asp-for="ProductModels.Description" type="text" class="form-control">
</div>
</div>
<button type="button" onclick="myFunction()">Add Product</button>
<table id="myTable">
<tr>
<td>Name</td>
<td>Model </td>
<td>Brand</td>
</tr>
<tr>
<td>
<input type="text" name="productModels.Products[0].Name" />
</td>
<td>
<input type="text" name="productModels.Products[0].Model" />
</td>
<td>
<input type="text" name="productModels.Products[0].Brand" />
</td>
</tr>
<tr>
<td>
<input type="text" name="productModels.Products[1].Name" />
</td>
<td>
<input type="text" name="productModels.Products[1].Model" />
</td>
<td>
<input type="text" name="productModels.Products[1].Brand" />
</td>
</tr>
</table>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Create</button>
</div>
</div>
</form>
Backend:
[BindProperty]
public InvoiceModel ProductModels { get; set; }
public void OnGet()
{
}
public void OnPost(InvoiceModel productModels)
{
//var Name = Request.Form["Name"];
//var Description = Request.Form["Description"];
var Name = Request.Form["Name"];
var Model = Request.Form["Model"];
var Brand = Request.Form["Brand"];
ViewData["confirmation"] = $"{Name},{Model},{Brand}";
}
Result:

Related

ASP .NET core Razor page - Avoid refreshing the page

I have tow buttons in one Form, first button for add website info to a local table and second button for add social media info to another table, after all info added locally,
then I can click on 'add all info' button for add all info in same time to database.
My question is how can I add info to a table without refreshing the page?
AddAllInfo.cshtml:
<div class="col-md-6 mb-3">
<label class="form-label" asp-for="NewWebSiteInfo.websiteName">Website URL</label>
<input type="text" asp-for="NewWebSiteInfo.websiteName" class="form-control" />
</div>
<div class="col-md-6 mb-3">
<label class="form-label" asp-for="NewWebSiteInfo.websiteUrl">Website URL</label>
<input type="text" asp-for="NewWebSiteInfo.websiteUrl" class="form-control" />
</div>
<button type="submit" validatedisable="True" asp-page-handler="AddWebsiteInfo" class="btn btn-primary" >Add Website info</button>
<div class="mb-3">
#if (AddInfoModel.WebSitelist.Count > 0)
{
<div class="col-12 border p-3">
<table class="table table-striped table-bordered">
<thead style="background-color:lightgray">
<tr>
<th>WebsiteName</th>
<th>websiteURL</th>
</tr>
</thead>
<tbody>
#foreach (Website item in AddAllInfoModel.WebSitelist)
{
<tr>
<td>#item.WebsiteName</td>
<td>#item.websiteURL</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</br>
</br>
<div class="col-md-6 mb-3">
<label class="form-label" asp-for="NewSocialMediaInfo.SocialMediaName">Social Media</label>
<input type="text" asp-for="NewSocialMediaInfo.SocialMediaName" class="form-control" />
</div>
<div class="col-md-6 mb-3">
<label class="form-label" asp-for="NewSocialMediaInfo.SocialMediaAccount">Account</label>
<input type="text" asp-for="NewSocialMediaInfo.SocialMediaAccount" class="form-control" />
</div>
<button type="submit" validatedisable="True" asp-page-handler="AddSocialMediaInfo" class="btn btn-primary" >Add socil Media info</button>
<div class="mb-3">
#if (AddInfoModel.SocialMedialist.Count > 0)
{
<div class="col-12 border p-3">
<table class="table table-striped table-bordered">
<thead style="background-color:lightgray">
<tr>
<th>SocialMediaName</th>
<th>SocialMediaAccount</th>
</tr>
</thead>
<tbody>
#foreach (SocialMedia item in AddAllInfoModel.SocialMedialist)
{
<tr>
<td>#item.SocialMediaName</td>
<td>#item.SocialMediaAccount</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</br>
<div class="col-4 offset-2">
<button type="submit" class="btn btn-primary form-control"> Add all info </button>
</div>
</form> ```
AddAllInfo.cshtml.cs:
public void OnPostAddSocialMediaInfo()
{
SocialMedialist.Add(new SocialMedia { SocialMediaName = NewSocialMediaInfo.SocialMediaName,
SocialMediaAccount=NewSocialMediaInfo.SocialMediaAccount});
}
public void OnPostAddWebsiteInfo()
{
WebSitelist.Add(new WebSite { WebSiteName = NewWebSiteInfo.WebsiteName,
websiteUrl =NewWebSiteInfo.websiteUrl});
}
You can use js to pass data to handler,and then use js to add html to tbody,here is a demo to add data to table without refresh the page:
<div class="col-md-6 mb-3">
<label class="form-label" asp-for="NewWebSiteInfo.websiteName">Website URL</label>
<input type="text" asp-for="NewWebSiteInfo.websiteName" class="form-control" />
</div>
<div class="col-md-6 mb-3">
<label class="form-label" asp-for="NewWebSiteInfo.websiteUrl">Website URL</label>
<input type="text" asp-for="NewWebSiteInfo.websiteUrl" class="form-control" />
</div>
<input type="button" onclick="AddWebsiteInfo()" class="btn btn-primary" value="Add Website info">
<div class="mb-3">
#if (AddInfoModel.WebSitelist.Count > 0)
{
<div class="col-12 border p-3">
<table class="table table-striped table-bordered">
<thead style="background-color:lightgray">
<tr>
<th>WebsiteName</th>
<th>websiteURL</th>
</tr>
</thead>
<tbody>
#foreach (Website item in AddAllInfoModel.WebSitelist)
{
<tr>
<td>#item.WebsiteName</td>
<td>#item.websiteURL</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</br>
</br>
<div class="col-md-6 mb-3">
<label class="form-label" asp-for="NewSocialMediaInfo.SocialMediaName">Social Media</label>
<input type="text" asp-for="NewSocialMediaInfo.SocialMediaName" class="form-control" />
</div>
<div class="col-md-6 mb-3">
<label class="form-label" asp-for="NewSocialMediaInfo.SocialMediaAccount">Account</label>
<input type="text" asp-for="NewSocialMediaInfo.SocialMediaAccount" class="form-control" />
</div>
<input type="button" onclick="AddSocialMediaInfo()" class="btn btn-primary" value="Add socil Media info">
<div class="mb-3">
#if (AddInfoModel.SocialMedialist.Count > 0)
{
<div class="col-12 border p-3">
<table class="table table-striped table-bordered">
<thead style="background-color:lightgray">
<tr>
<th>SocialMediaName</th>
<th>SocialMediaAccount</th>
</tr>
</thead>
<tbody>
#foreach (SocialMedia item in AddAllInfoModel.SocialMedialist)
{
<tr>
<td>#item.SocialMediaName</td>
<td>#item.SocialMediaAccount</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</br>
<div class="col-4 offset-2">
<input type="button" onclick="AddAllInfo()" class="btn btn-primary" value="Add all info">
</div>
</form> ```
js:
#section Scripts{
<script>
function AddWebsiteInfo() {
var NewWebSiteInfo = {
'websiteName': $("#NewWebSiteInfo_websiteName").val(),
'websiteUrl': $("#NewWebSiteInfo_websiteUrl").val()
};
$.ajax({
type: 'POST',
url: '?handler=AddWebsiteInfo',
data: NewWebSiteInfo,
headers: { "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val() },
dataType: 'json',
success: function (data) {
var html = "<tr><td>" + data.websiteName + "</td><td>" + data.websiteUrl + "</td></tr>";
$("tbody")[0].innerHTML = $("tbody")[0].innerHTML + html;
}
});
}
function AddSocialMediaInfo() {
var NewAddSocialMediaInfo = {
'SocialMediaName': $("#NewSocialMediaInfo_SocialMediaName").val(),
'SocialMediaAccount': $("#NewSocialMediaInfo_SocialMediaAccount").val()
};
$.ajax({
type: 'POST',
url: '?handler=AddSocialMediaInfo',
data: NewAddSocialMediaInfo,
headers: { "RequestVerificationToken": $('input[name="__RequestVerificationToken"]').val() },
dataType: 'json',
success: function (data) {
var html = "<tr><td>" + data.socialMediaName + "</td><td>" + data.socialMediaAccount + "</td></tr>";
$("tbody")[1].innerHTML = $("tbody")[1].innerHTML + html;
}
});
}
function AddAllInfo() {
AddWebsiteInfo();
AddSocialMediaInfo();
}
</script>
}
handler:
[BindProperty]
public WebSiteInfo NewWebSiteInfo { get; set; }
[BindProperty]
public SocialMediaInfo NewSocialMediaInfo { get; set; }
public void OnGet()
{
}
public JsonResult OnPostAddWebsiteInfo()
{
WebSitelist.Add(new WebSite { WebSiteName = NewWebSiteInfo.WebsiteName,
websiteUrl =NewWebSiteInfo.websiteUrl});
return new JsonResult(NewWebSiteInfo);
}
public JsonResult OnPostAddSocialMediaInfo()
{
SocialMedialist.Add(new SocialMedia { SocialMediaName = NewSocialMediaInfo.SocialMediaName,
SocialMediaAccount=NewSocialMediaInfo.SocialMediaAccount});
return new JsonResult(NewSocialMediaInfo);
}
The best way to do this is to run the post request in javascript, this will not refresh the page.
2nd option is by calling the get method again at the end of the post method and then passing the model with the data to the get. So that when the page refreshes the data is filled in again

The Form post null value to the pagemodel

trying to submit the commenting form,but the form send null data to my razor page.
it is the html:
<div class="comment-form-container">
<h6>Leave a Comment</h6>
<form method="post">
<div class="input-prepend">
<span class="add-on"><i class="icon-user"></i></span>
<input name="Name" class="span4" id="Name" size="16" type="text" placeholder="Name">
</div>
<div class="input-prepend">
<span class="add-on"><i class="icon-envelope"></i></span>
<input name="Email" class="span4" id="Email" size="16" type="email" placeholder="Email Address">
</div>
<input type="hidden" name="ArticleId" id="ArticleId" value="#Model.articleQueryView.Id" />
<textarea class="span6" name="Message" id="Message"></textarea>
<div class="row">
<div class="span2">
<input type="submit" class="btn btn-inverse" value="Post My Comment">
</div>
</div>
</form>
</div>
and here is the CsHtml code,when in hover the "comment" i just see the null and 0 value:
public class blog_singleModel : PageModel
{
public ArticleQueryView articleQueryView { get; set; }
private readonly ICommentApplication _commentApplication;
private readonly IArticleQuery _query;
public blog_singleModel(IArticleQuery query, ICommentApplication commentApplication)
{
_query = query;
_commentApplication = commentApplication;
}
public void OnGet(int id)
{
articleQueryView = _query.GetArticle(id);
}
public RedirectToPageResult OnPost(AddNewComment comment)
{
_commentApplication.Add(comment);
return RedirectToPage("./blog_single", new { id = comment.ArticleId });
}
}
}
comment.Id is 0 is because your articleQueryView.Id is 0.Name,Email,Message is null is because you did not set the values of them in form.I think your code is correct,but you did not set the values.
sample:

How to pass HTML label value to PageModel on form submission

Not sure if it can be done, but how would I assign an HTML label value to a BindProperty.
In my PageModel, I have a List of DateTimes I generate and this list is used in the Razor Page and displayed on rows. (submissionDates is the list)
<tbody>
#foreach (var item in Model.submissionDates)
{
<tr>
<td>
<div class="form-group">
<label asp-for="Timesheet.TimesheetStart" class="control-label">#item.Date</label>
</div>
</td>
<td>
<div class="form-group">
<label asp-for="Timesheet.TimesheetNotes" class="control-label">Submission Notes</label>
<textarea asp-for="Timesheet.TimesheetNotes" class="form-control" id="NotesTextArea" rows="3"></textarea>
</div>
</td>
<td>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</td>
</tr>
}
</tbody>
In the OnPost of the PageModel I need the date from the row submitted to add to the Model.
public async Task<IActionResult> OnPostAsync()
{
string weekSelected = Request.Form["Timesheet.TimesheetStart"];
var emptyTimesheet = new Timesheet()
{
Submitted = DateTime.Now,
TimesheetStart = DateTime.Parse(weekSelected),
TimesheetEnd = TimesheetStart.AddDays(7),
ColleagueID = UserColleague,
BranchID = (int)branchFilter
};
if ( await TryUpdateModelAsync<Timesheet>(
emptyTimesheet,
"timesheet",
s => s.TimesheetNotes
))
{
_context.Timesheets.Add(emptyTimesheet);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
I have tried using Model Binding like I have with TimesheetNotes but that was null, the attempt shown above is reading straight from the Form Response and this is also null.
Am I going about this the wrong way or in my scenario is getting that label value not possible?
You can use a hidden input,which name is Timesheet.TimesheetStart,.net core bind data with name attribute.And you need to set its value with #item.Date.Then you need to put form outside <tr></tr>,so that when you click the button,it will only post the values of the current row rather than all the rows.
<tbody>
#foreach (var item in Model.submissionDates)
{
<form method="post">
<tr>
<td>
<div class="form-group">
<label asp-for="Timesheet.TimesheetStart" class="control-label">#item.Date</label>
<input hidden name="Timesheet.TimesheetStart" value=#item.Date>
</div>
</td>
<td>
<div class="form-group">
<label asp-for="Timesheet.TimesheetNotes" class="control-label">Submission Notes</label>
<textarea asp-for="Timesheet.TimesheetNotes" class="form-control" id="NotesTextArea" rows="3"></textarea>
</div>
</td>
<td>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</td>
</tr>
</form>
}
</tbody>
result:
Only INPUT elements will be available in the Request.Form. You can use an HTML Hidden field for each label. With hidden fields, you can do model binding as well.
<div class="form-group">
<label asp-for="Timesheet.TimesheetStart" class="control-label">#item.Date</label>
<input type="hidden" asp-for="Timesheet.TimesheetStart" />
</div>

Passing ViewModel to a View not clearing down?

I have a project wrote in ASP.Net Core 3.1. I have a view model which has a list of all my surveyors and a single model of a surveyor. The list is used in my table to show all entries and the single model is used to bind to input controls in my Razor View. On submit I all an update method in my controller and check if the id of the passed model is 0 or not. If 0 I add it to my database and if not I update that entry. This all works fine. I then return to the view with a new viewmodel. My problem is it is not clearing the single entry so all the input controls still have the previously added entry.
View:
#model SurveyorViewModel
<link href="~/CSS/Surveyor.css" rel="stylesheet" />
<form id="SurveyorList" asp-controller="Surveyor" asp-action="update" method="post" class="mt-3">
<input type="hidden" asp-for="Surveyor.PK_Id" />
<div id="cover" class="container vertical-center col-xl-5 col-lg-6 col-md-8 col-sm-10 mx-auto form p-4" style="box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);">
<div id="divSurveyorList" class="table mt-2">
<table id="surveyorList" class="table table-bordered table-hover">
<thead>
<tr>
<th>
<span style="font-family:Verdana; font-size: 12px; width: 150px;">Surveyors</span>
</th>
<th style="font-size: 12px; width: 40px; color:#6d6d6d;">Edit/Delete</th>
</tr>
</thead>
<tbody>
#if (Model != null)
{
foreach (var surveyor in Model.SurveyorList)
{
<tr style="color: #6d6d6d; font-family: Verdana; font-size:12px; font-weight:100;">
<td>
#surveyor.FullName
</td>
<td>
<div class="btn-group">
<a class="btn btn-primary" asp-controller="Surveyor" asp-action="EditSurveyor" asp-route-Id=#surveyor.PK_Id><span class="fas fa-pencil-alt"></span></a>
<a class="btn btn-danger" asp-controller="Surveyor" asp-action="DeleteSurveyor" asp-route-Id=#surveyor.PK_Id )><span class="fas fa-trash-alt"></span></a>
</div>
</td>
</tr>
}
}
</tbody>
</table>
</div>
<div class="row align-items-start mt-1">
<div class="col-4">
<label asp-for="Surveyor.FullName" class="col-sm-2 col-md-12 nopadding col-form-label">Full Name</label>
</div>
<div class="col-8">
<input asp-for="Surveyor.FullName" class="form-control" placeholder="Full Name" />
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<label asp-for="Surveyor.Address_Line_1" class="col-sm-2 col-md-4 col-form-label">Address</label>
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<input asp-for="Surveyor.Address_Line_1" class="form-control" placeholder="Address Line 1" />
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<input asp-for="Surveyor.Address_Line_2" class="form-control" placeholder="Address Line 2" />
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<input asp-for="Surveyor.Town" class="form-control" placeholder="Town" />
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<input asp-for="Surveyor.Postcode" class="form-control" placeholder="Post Code" />
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<label asp-for="Surveyor.ContactNo" class="col-sm-2 col-md-4 col-lg-6 col-form-label">Telephone No.</label>
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<input asp-for="Surveyor.ContactNo" class="form-control" placeholder="Telephone No." />
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<label asp-for="Surveyor.Email_Address" class="col-sm-2 col-md-4 col-lg-6 col-form-label">Email Address</label>
</div>
</div>
<div class="row align-items-start">
<div class="col-12">
<input asp-for="Surveyor.Email_Address" class="form-control" placeholder="Email Address" />
</div>
</div>
<div class="d-flex flex-row-reverse">
<button type="submit" class="btn btn-primary mt-2"><span class="fas fa-plus-circle"></span> Update</button>
<a class="btn btn-primary float-right mt-2" asp-controller="Surveyor" asp-action="Create" style="width: 100px; margin-right: 20px;" )><span class="fas fa-plus-circle"></span><span> Create</span></a>
<a class="btn btn-primary mt-2 mr-2" asp-controller="Surveyor" asp-action="Back" style="width: 90px;" )><span class="fas fa-hand-point-left"></span><span> Back</span></a>
</div>
</div>
</form>
ViewModel:
public class SurveyorViewModel
{
public IEnumerable<Surveyor> SurveyorList { get; set; }
public Surveyor Surveyor { get; set; }
}
Model:
public class Surveyor
{
[Required]
[Key]
public int PK_Id { get; set; }
[Required]
[MaxLength(120)]
public string FullName { get; set; }
public string Address_Line_1 { get; set; }
public string Address_Line_2 { get; set; }
public string Town { get; set; }
public string Postcode { get; set; }
public string ContactNo { get; set; }
[EmailAddress]
public string Email_Address { get; set; }
public bool Archived { get; set; }
}
Controller Methods:
private SurveyorViewModel PopulateLists()
{
SurveyorViewModel SurveyorViewModel = new SurveyorViewModel();
SurveyorViewModel.SurveyorList = context.Surveyors.ToList();
SurveyorViewModel.Surveyor = new Surveyor();
return SurveyorViewModel;
}
public IActionResult update(Surveyor surveyor)
{
if (ModelState.IsValid)
{
if (surveyor.PK_Id == 0)
{
surveyor = addSurveyor(surveyor);
}
else
{
surveyor = updateSurveyor(surveyor);
}
}
SurveyorViewModel surveyorViewModel = PopulateLists();
surveyorViewModel.Surveyor = surveyor;
return View("Surveyor", surveyorViewModel);
}
I've altered my update routine which when doing the add the PK_Id has a value which is passed to the view but when I inspect the elements my hidden field has a value of zero. I did a bit of research and found the following bit of code, when doing the inspect my hidden value has the correct PK_Id value but this is not passed to my controller???
<input type="hidden" value="#Model.Surveyor.PK_Id" name="PK_Id" />
Many thanks for any help,
My problem is it is not clearing the single entry so all the input controls still have the previously added entry.
In the following code,you set surveyorViewModel.Surveyor with the previously added entry
SurveyorViewModel surveyorViewModel = PopulateLists();
surveyorViewModel.Surveyor = surveyor;
return View("Surveyor", surveyorViewModel);
You can try to remove surveyorViewModel.Surveyor = surveyor;,so that the input controls will not have the previously added entry.
I've altered my update routine which when doing the add the PK_Id has a value which is passed to the view but when I inspect the elements my hidden field has a value of zero. I did a bit of research and found the following bit of code, when doing the inspect my hidden value has the correct PK_Id value but this is not passed to my controller???
Where you pass the PK_Id to view,and where you have the code <input type="hidden" value="#Model.Surveyor.PK_Id" name="PK_Id" />?
I managed to find a solution from an old post on here. Basically before I populate a new instance of my viewmodel I use ModelState.Remove("PK_Id"). From what I have read the view will use the default that was past when the post was executed meaning in my case because it was a new entry it would have been 0. By including the remove statement it uses the new Id that I have set in my model.

MVC ASP.NET Core 3.1 POST to Create method does not populate model

I have used Visual Studio 2019 to create a Create view (right-click > Add > View) and set the correct model when doing so. After the Create view was created, I added code to the Create HttpPost ActionMethod and just tried to run the app. The ActionMethod is hit, but the data model that is supposed to be passed in to the method is not populated; everything is nulls despite having entered data in the browser. What is weird is that I have other pages in the app created the exact same way (even other Creates on other Areas) that work fine. What am I missing?
Model:
public class BondsAllProjectsModel
{
[Key]
public int ProjectID { get; set; }
[Display(Name = "Project Name")]
[Required (ErrorMessage = "Project Name is required.")]
[StringLength(50)]
public string ProjectName { get; set; }
[Display(Name = "Project Name Alias")]
[StringLength(50)]
public string ProjectNameAlias { get; set; }
[StringLength(50)]
public string Municipality { get; set; }
[StringLength(4)]
public string ProjectCode { get; set; }
public bool IsProject { get; set; }
[RegularExpression(#"^[0-9]*\.?[0-9]*", ErrorMessage = "Only numbers are allowed.")]
public double? ProjectAcres { get; set; }
[RegularExpression(#"\d+", ErrorMessage = "Only numbers are allowed.")]
public double? ProjectUnits { get; set; }
public int? MunicipalityID { get; set; }
[DataType(DataType.Date, ErrorMessage = "Project Completed must be a valid date.")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime? ProjectCompleted { get; set; }
[MaxLength(100)]
public string CurrentNotes { get; set; }
public int? ProjectTypeID { get; set; }
public bool Discontinued { get; set; }
}
View:
#model BOnlineMVC.Models.BondsAllProjectsModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>BondsAllProjectsModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProjectID" class="control-label"></label>
<input asp-for="ProjectID" class="form-control" />
<span asp-validation-for="ProjectID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectName" class="control-label"></label>
<input asp-for="ProjectName" class="form-control" />
<span asp-validation-for="ProjectName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectNameAlias" class="control-label"></label>
<input asp-for="ProjectNameAlias" class="form-control" />
<span asp-validation-for="ProjectNameAlias" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Municipality" class="control-label"></label>
<input asp-for="Municipality" class="form-control" />
<span asp-validation-for="Municipality" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectCode" class="control-label"></label>
<input asp-for="ProjectCode" class="form-control" />
<span asp-validation-for="ProjectCode" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsProject" /> #Html.DisplayNameFor(model => model.IsProject)
</label>
</div>
<div class="form-group">
<label asp-for="ProjectAcres" class="control-label"></label>
<input asp-for="ProjectAcres" class="form-control" />
<span asp-validation-for="ProjectAcres" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectUnits" class="control-label"></label>
<input asp-for="ProjectUnits" class="form-control" />
<span asp-validation-for="ProjectUnits" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MunicipalityID" class="control-label"></label>
<input asp-for="MunicipalityID" class="form-control" />
<span asp-validation-for="MunicipalityID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectCompleted" class="control-label"></label>
<input asp-for="ProjectCompleted" class="form-control" />
<span asp-validation-for="ProjectCompleted" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CurrentNotes" class="control-label"></label>
<input asp-for="CurrentNotes" class="form-control" />
<span asp-validation-for="CurrentNotes" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectTypeID" class="control-label"></label>
<input asp-for="ProjectTypeID" class="form-control" />
<span asp-validation-for="ProjectTypeID" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Discontinued" /> #Html.DisplayNameFor(model => model.Discontinued)
</label>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
Controller:
[HttpPost]
public ActionResult Create(BondsAllProjectsModel projectData)
{
try
{
if (ModelState.IsValid)
{
int newProjectID = bDAL.AddProject(projectData);
return Redirect("~/Bonds/ProjectMaintenance/Create2/" + newProjectID);
}
else
{
return View();
}
}
catch
{
return View();
}
}
You can try to explicitly specify where model binder should looks when binding data by using
[FromForm] attribute
click here for more info
top of the view add #model namespace.BondsAllProjectsModel
<div class="form-group">
<label asp-for="ProjectID" class="control-label"></label>
<input asp-for="#Model.ProjectID" class="form-control" />
<span asp-validation-for="ProjectID" class="text-danger"></span>
</div>
Just add this line in the top of your create view:
#model YourNamespace.BondsAllProjectsModel
There is no need to modify anything else.