Null reference exception after POST [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I have a form, which gets some values from model. Then, in POST the data user entered are handled and user is redirected to another page. However everytime I post the form, I get null-reference exeption. Where do I make a mistake?
None of the other questions asking about this didn´t solve my problem, that´s why I am asking again with my specific code.
The exeptions are at foreach loops - Model.Cart, Model.ShippingOptionsm etc.
#model CheckoutViewModel
#{
ViewBag.Title = "Checkout";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Checkout</h2>
<table class="table">
<thead>
<tr>
<td>#</td>
<td>Name</td>
<td>Quantity</td>
<td>Price</td>
<td>Total price</td>
</tr>
</thead>
#foreach (CartItem i in Model.Cart)
{
<tr>
<td>#Html.Action("ProductPosition", "Cart", new { item = i })</td>
<td>#i.Name</td>
<td>#i.Quantity</td>
<td>#i.Price €</td>
<td>#i.Total €</td>
</tr>
}
</table>
<h3>Total: #Html.Action("TotalPrice", "Cart") €</h3>
#using (Html.BeginForm("Checkout", "Cart"))
{
#Html.AntiForgeryToken()
<h2>Address</h2>
<div class="form-group">
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Name)
</div>
<div class="form-group">
#Html.LabelFor(m => m.Address)
#Html.TextBoxFor(m => m.Address, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Address)
</div>
<div class="form-group">
#Html.LabelFor(m => m.City)
#Html.TextBoxFor(m => m.City, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.City)
</div>
<div class="form-group">
#Html.LabelFor(m => m.PostNumber)
#Html.TextBoxFor(m => m.PostNumber, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.PostNumber)
</div>
<div class="form-group">
#Html.LabelFor(m => m.State)
#Html.TextBoxFor(m => m.State, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.State)
</div>
<h2>Shipping</h2>
foreach (var i in Model.ShippingOptions)
{
<div class="radio">
#Html.RadioButtonFor(m => m.ShippingOption, i.Id) #i.Name - #i.Price €
</div>
}
#Html.ValidationMessageFor(m => m.ShippingOption);
<h2>Payment</h2>
foreach (var i in Model.PaymentOptions)
{
<div class="radio">
#Html.RadioButtonFor(m => m.PaymentOption, i.Id) #i.Name
</div>
}
#Html.ValidationMessageFor(m => m.PaymentOption);
<button type="submit" class="btn btn-primary">Continue</button>
}
#section scripts
{
#Scripts.Render("~/bundles/jqueryval")
}
Controller:
public ActionResult Checkout()
{
if (Session["cart"] == null)
return RedirectToAction("Index");
var checkout = new CheckoutViewModel()
{
Cart = (List<CartItem>)Session["cart"],
PaymentOptions = _context.PaymentOptions.ToList(),
ShippingOptions = _context.ShippingOptions.ToList()
};
return View(checkout);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Checkout(CheckoutViewModel m)
{
if (!ModelState.IsValid)
return View();
//TODO: handle data
return RedirectToAction("OrderSuccess");
}

Add model declaration at the top of the page
#model CheckoutViewModel

Related

Bootstrap Modal showing with blank edit data

I am using a modal bootstrap but my data is not loading into the bootstrap even though I am passing it trough from the controller.
My Grid pulls in data from a list for Notes Object
#foreach (var notes in Model) {
<tr>
<td>#notes.LastModifedDate</td>
<td>#notes.LastModifiedBy</td>
<td>#notes.Notes </td>
<td>
<p>#notes.Id</p> <i class="glyphicon glyphicon-pencil"></i>Edit
|<p>#notes.Id</p> <i class="glyphicon glyphicon-trash"></i>Delete
</td>
</tr>
}
I am using the following to Return the data to the popup.
public ActionResult NotesEditPopupPartial(int id ) {
var record = _context.MISNotes.Where(w => w.Id == id).FirstOrDefault();
return PartialView("NotesEditPopupPartial", record);
}
And using the following call to load the data but the data is not in the model for me to edit the text fields are still blank.
<script>
var AddOrEditNotes = function (id) {
var url = "/MISOjbects/NotesEditPopupPartial?id=" + id;
$("#myModalBodyDiv1").load(url, function () {
$("#MyEditUpateModal").modal("show");
})
}
</script>
<script>
function EditModal(id) {
$("#editMode").val(id);
}
</script>
My Modal
<div class="modal fade" id="NotesEditPopupPartial">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
×
<h3 class="modal-title">Notes Entry</h3>
</div>
<div class="modal-body" id="myModalBodyDiv1">
<div>
<input type="text" name="linkId" id="linkId" />
#using (Html.BeginForm("SaveCaseNotes", "MISObjects", FormMethod.Post, new { #id = "myNotesEditForm", #name = "myNotesEditForm", enctype = "multipart/form-data" })) {
<form id="myForm" asp-action="">
<div asp-validation-summary="All" class="text-danger"></div>
#Html.LabelFor(model => model.Title)
#Html.TextBoxFor(model => model.Title, new { #class = "form-control", #placeholder = "Title" })
#Html.ValidationMessageFor(model => model.Title, string.Empty, new { #style = "color:red;" })
<div class="form-group">
#Html.LabelFor(model => model.Notes, new { #class = "col-lg-2 control-label" })
<div class="col-lg-9">
#Html.TextAreaFor(model => model.Notes, new { #class = "form-control", #row = 5 })
</div>
</div>
#Html.LabelFor(model => model.DateActioned)
#Html.EditorFor(model => model.DateActioned, new { htmlAttributes = new { #class = "form-control datetimepicker" } })
#Html.ValidationMessageFor(model => model.DateActioned)
#Html.DisplayNameFor(model => model.LastModifiedBy)
#Html.LabelFor(model => model.LastModifiedBy)
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
<div class="modal-footer">
<button type="submit" id="btnEditSave" class="btn btn-primary">Save</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</form>
}
</div>
<div style="text-align:center;display:none" id="loaderDiv">
<img src="~/Content/InternetSlowdown_Day.gif" width="150" />
</div>
</div>
</div>
Wrap your modal with div:
<div id="modalWrapper">
<div class="modal fade" id="NotesEditPopupPartial">
...
</div>
</div>
and then load and show the modal:
$("#modalWrapper").load(url, function () {
$("#NotesEditPopupPartial").modal("show");
})
<script>
var AddOrEditNotes = function (id) {
var url = "/MISOjbects/NotesEditPopupPartial?id=" + id;
$("#myModalBodyDiv1").load(url, function () {
$("#MyEditUpateModal").modal("show");
})
}
</script>
<script>
function EditModal(id) {
$("#editMode").val(id);
}
</script>
According to your code, I didn't find where you call the AddorEditNotes function to call the NotesEditPopupPartial action and display the partial, and in the EditModel method, you also didn't call the action method to show the partial, so, the popup modal is blank.
In my opinion, I suggest you put the bootstrap Modal on the main page (with note grid), because the trigger button (Edit hyperlink) is on this page. Then, in the hyperlink click event, you could use JQuery Ajax to call the action method and load the partial view and put it on the Bootstrap Modal.
Sample code as below:
Code in the Main page:
#model IEnumerable<NetMvcSample.Models.Note>
#{
ViewBag.Title = "ShowNotes";
}
<h2>ShowNotes</h2>
<script>
function EditModal(id) {
event.preventDefault(); // prevent the hyperlink default event.
$.ajax({
type: "POST",
url: "/Home/NotesEditPopupPartial", //change the url to your owns.
data: '{id: '+ id + "}",
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data)
//put the partial view in the modal content.
$('#myModalBodyDiv1').html(data);
},
failure: function (response) {
console.log(response.responseText);
},
error: function (response) {
console.log(response.responseText);
}
});
}
</script>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.LastModifyDate)
</th>
<th>
#Html.DisplayNameFor(model => model.LastModifiedBy)
</th>
<th>
#Html.DisplayNameFor(model => model.Notes)
</th>
<th></th>
</tr>
#foreach (var notes in Model) {
<tr>
<td>
#notes.LastModifyDate
</td>
<td>
#notes.LastModifiedBy
</td>
<td>
#notes.Notes
</td>
<td>
<p>#notes.Id</p> <i class="glyphicon glyphicon-pencil"></i>Edit
|<p>#notes.Id</p> <i class="glyphicon glyphicon-trash"></i>Delete
</td>
</tr>
}
</table>
<div class="modal fade" id="NotesEditPopupPartial">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
×
<h3 class="modal-title">Notes Entry</h3>
</div>
<div class="modal-body" id="myModalBodyDiv1">
</div>
</div>
</div>
</div>
Code in the controller method:
[HttpPost]
public ActionResult NotesEditPopupPartial(int id)
{
var record = GetNotes().Where(c => c.Id == id).FirstOrDefault();
return PartialView("NotesEditPopupPartial", record);
}
public ActionResult SaveCaseNotes(Note note)
{
// do something
return View();
}
Code in the NotesEditPopupPartial view (NotesEditPopupPartial.cshtml):
#model NetMvcSample.Models.Note
#using (Html.BeginForm("SaveCaseNotes", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Note</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.LastModifyDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LastModifyDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LastModifyDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LastModifiedBy, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.LastModifiedBy, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LastModifiedBy, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Notes, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Notes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Notes, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The output like this:

Object Reference, Unable to find User ID

Within my project I am trying to change the users ability from only being able to "Edit" an address field to being able to "Create" and "Edit" if it already exist within the same view. I receive the following server error Click Here.
I then ran it through the debugger and received the following result Click Here Too!. I can see that my User ID is being passed as null in the controller which creates the server error. But after following Mr. Pratt advice I can see the User Id is being passed into the View I am unsure why this is happening or where I've made the mistake.
Controller
// GET: /UserProfile/Edit/5
public ActionResult Edit(int id)
{
var userProfile = db.UserProfiles.FirstOrDefault(up => up.UserId == id);
var query = from o in db.UserProfiles
where o.Address != null
select o;
ViewBag.Query = query;
// if the user is not an admin
if (!User.IsInRole("admin"))
{
// look up current user id
var currentUserId = (int)System.Web.Security.Membership.GetUser().ProviderUserKey;
// check to make sure that the user profile is attached to the currently logged in user
if (userProfile.UserId == currentUserId)
{
return View(userProfile);
}
else
{
throw new Exception("you cannot access a user profile that is not your own");
}
}
// the user is an admin, so let them view the profile
return View(userProfile);
}
View
#model YardLad.Models.Domain.UserProfile
#{
ViewBag.Title = "Edit Profile";
}
<h2>Edit Profile</h2>
#if (User.IsInRole("contractor") || User.IsInRole("contractor2"))
{
<style>
#content a {
color: cornflowerblue;
}
#content a:hover {
color: cornflowerblue;
}
</style>
}
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.HiddenFor(m => m.UserId)
#Html.HiddenFor(m => m.AcceptSMS)
#Html.HiddenFor(m => m.IsActive)
#Html.HiddenFor(m => m.LastLoginDate)
#Html.HiddenFor(m => m.AddressId)
<div class="editor-label">
Name
</div>
<div class="editor-field">
#Html.EditorFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.LastName)
#Html.ValidationMessageFor(m => m.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Phone)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Phone)
#Html.ValidationMessageFor(m => m.Phone)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Mobile)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Mobile)
#Html.ValidationMessageFor(m => m.Mobile)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DateOfBirth)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateOfBirth)
#Html.ValidationMessageFor(model => model.DateOfBirth)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Gender)
</div>
<div class="editor-field">
#Html.RadioButton("Gender", "male") male
#Html.RadioButton("Gender", "female") female
#Html.ValidationMessageFor(m => m.Gender)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AddressId, "Address")
</div>
var address = ViewContext.ViewData.ModelState["Address"];
if (address != null && address.Errors.Count() > 0)
{
#Html.ActionLink("Add a billing address", "Create", "Address",
new { returnUrl = "UserProfile/Edit", userId = Model.UserId }, null)
}
else
{
<div class="editor-field">
#Html.DisplayFor(m => m.Address.Line1)<br />
#*#if (Model.Address.Line2 != null && address.Any())
{
#Html.DisplayFor(m => m.Address.Line2)<br />
}*#
#Html.DisplayFor(m => m.Address.City), #Html.DisplayFor(m => m.Address.State.Abbreviation) #Html.DisplayFor(m => m.Address.PostalCode)
</div>
if (ViewBag.Query != null || ViewBag.Query != "" )
{
#Html.ActionLink("Add an address", "Create", "Address", new { id = Model.AddressId, returnUrl = "UserProfile/Edit", userId = Model.UserId }, null)
}
else
{
#Html.ActionLink("Edit address", "Create", "Address", new { id = Model.AddressId, returnUrl = "UserProfile/Edit", userId = Model.UserId }, null)
}
}
#*<div class="editor-field">
<span>#Html.CheckBoxFor(m => m.AcceptSMS)</span>
<span class="editor-label">
#Html.LabelFor(m => m.AcceptSMS, "Accept SMS Service")
</span>
<p style="width: 50%; min-width: 300px;">
this free service allows you to send and receive text updates for easier access to making payments, scheduling reoccurring services, rating services, etc.<br />
<span style="font-size: .9em; color: forestgreen;">standard SMS charges still apply (based on your mobile carrier and plan)</span>
</p>
</div>*#
<p>
<input type="submit" value="Save" />
</p>
}
<div>
#Html.ActionLink("Back to My Account", "MyAccount", "Account")
</div>
userProfile is null. You need to do proper null-checking:
var userProfile = db.UserProfiles.FirstOrDefault(up => up.UserId == id);
if (userProfile == null)
{
return new HttpNotFoundResult();
}

Asp.net MVC 5 ModelValidation is not working

So I have a ViewModel and the properties are all validated by having required attributes on them. When I submit the form it doesn't throw any error messages, i.e. name field is required.
I think I have figured where the problem is. Its in the submit button, how do I get the button to hit the HttpPost method of the ActionList because at the moment its not hitting that, hence the modelstate is not being validated.
View:
<h2>#ViewBag.Title</h2>
<hr>
<div class="well">
#using (Html.BeginForm("BookingDetails", "Booking"))
{
#Html.ValidationSummary(true)
<div class="form-group">
#Html.DisplayNameFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.FirstName)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.Surname)
#Html.TextBoxFor(m => m.Surname, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Surname)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.EmailAddress)
#Html.TextBoxFor(m => m.EmailAddress, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.EmailAddress)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.MobileNumber)
#Html.TextBoxFor(m => m.MobileNumber, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.MobileNumber)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.NumberOfPeople)
#Html.TextBoxFor(m => m.NumberOfPeople, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.NumberOfPeople)
</div>
<div class="form-group">
#Html.DisplayNameFor(m => m.Date)
#Html.TextBoxFor(m => m.Date, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Date)
</div>
<div>
#Html.DisplayNameFor(m => m.Time)
#Html.TextBoxFor(m => m.Time, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Time)
</div>
}
</div>
<div class="form-group">
#Html.ActionLink("Book my table", "BookingDetails", "Booking" new { #class ="btn btn-primary" })
</div>
Controller:
// GET:
public ActionResult BookingDetails()
{
return View();
}
// Post
[HttpPost]
public ActionResult BookingDetails(BookingDetailsViewModel model)
{
if(ModelState.IsValid)
{
return RedirectToAction("BookingConfirmation");
}
return View(model);
}
You have two problems that I can see.
Firstly, your button isn't a submit button. Its a link that sits outside of the form. You must move it into your form and make it a submit button (or write some javascript that submits the form on click):
<div>
#Html.DisplayNameFor(m => m.Time)
#Html.TextBoxFor(m => m.Time, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Time)
</div>
<input type="submit" class="btn btn-primary" /> <!-- move it inside the form -->
#* ^^^^ submit *#
} <!-- end of your form is here -->
Also, Html.ValidationSummary(true) will hide all of your property errors from your Validation Summary. This may not be what you want. If you run into that issue.. remove true from the call.

MVC 4 Optimistic concurrency exception

Having faced this issue (I wanted to allow an edit by using a bootstrap modal window, i'm using MVC4 and entity framework), when I want to save my changes, I have this error message since I'm using the modal window :
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
Here are my actions :
[HttpGet]
public ActionResult EditPerson(long id)
{
var person = db.Persons.Single(p => p.Id_Person == id);
ViewBag.Id_ProductPackageCategory = new SelectList(db.ProductPackageCategories, "Id_ProductPackageCategory", "Name", person.Id_ProductPackageCategory);
return PartialView("_EditPerson", person);
}
[HttpPost]
public ActionResult EditPerson(Person person)
{
ViewBag.Id_ProductPackageCategory = new SelectList(db.ProductPackageCategories, "Id_ProductPackageCategory", "Name", person.Id_ProductPackageCategory);
if (ModelState.IsValid)
{
ModelStateDictionary errorDictionary = Validator.isValid(person);
if (errorDictionary.Count > 0)
{
ModelState.Merge(errorDictionary);
return View(person);
}
db.Persons.Attach(person);
db.ObjectStateManager.ChangeObjectState(person, EntityState.Modified);
db.SaveChanges();
return View("Index");
}
return View(person);
}
My partial view :
#model BuSIMaterial.Models.Person
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Edit</h3>
</div>
<div>
#using (Ajax.BeginForm("EditPerson", "Person", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "table"
}))
{
#Html.ValidationSummary()
#Html.AntiForgeryToken()
<div class="modal-body">
<div class="editor-label">
First name :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.FirstName, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
Last name :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.LastName, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
National number :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NumNat, new { maxlength = 11 })
#Html.ValidationMessageFor(model => model.NumNat)
</div>
<div class="editor-label">
Start date :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.StartDate, new { #class = "datepicker", #Value = Model.StartDate.ToString("yyyy/MM/dd") })
#Html.ValidationMessageFor(model => model.StartDate)
</div>
<div class="editor-label">
End date :
</div>
<div class="editor-field">
#if (Model.EndDate.HasValue)
{
#Html.TextBoxFor(model => model.EndDate, new { #class = "datepicker", #Value = Model.EndDate.Value.ToString("yyyy/MM/dd") })
#Html.ValidationMessageFor(model => model.EndDate)
}
else
{
#Html.TextBoxFor(model => model.EndDate, new { #class = "datepicker" })
#Html.ValidationMessageFor(model => model.EndDate)
}
</div>
<div class="editor-label">
Distance House - Work (km) :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HouseToWorkKilometers)
#Html.ValidationMessageFor(model => model.HouseToWorkKilometers)
</div>
<div class="editor-label">
Category :
</div>
<div class="editor-field">
#Html.DropDownList("Id_ProductPackageCategory", "Choose one ...")
#Html.ValidationMessageFor(model => model.Id_ProductPackageCategory) <a href="../ProductPackageCategory/Create">
Add a new category?</a>
</div>
<div class="editor-label">
Upgrade? :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Upgrade)
#Html.ValidationMessageFor(model => model.Upgrade)
</div>
</div>
<div class="modal-footer">
<button class="btn btn-inverse" type="submit">Save</button>
</div>
}
Any idea on what's going on?
Try this first, just above #Html.ValidationSummary() in the partial view where you have the modal head, body and footer, place:
#Html.HiddenFor(model => model.PersonId) // or.Id whatever's in your model
This creates a hidden field in your view and sets model ID i.e. PK.

Fail to submit a list of object to the model binding using ICollection

i have added extra three input fields to my view to enable the system admin to submit four objects at the same time instead of one object at a time; the view looks as the following:-
#model Elearning.Models.Answer
#{
ViewBag.Title = "Create";
}
<div id = "partialWrapper">
#using (Ajax.BeginForm("Create", "Answer", new AjaxOptions
{
HttpMethod = "Post",
InsertionMode = InsertionMode.InsertAfter,
UpdateTargetId = "incrementanswer",
OnSuccess = "removePartial",
LoadingElementId = "progress2"
}))
{
<div id = "returnedquestion">
#Html.ValidationSummary(true)
<fieldset>
<legend>Answer here</legend>
<ol>
<li> <div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.TextBox("answer[0].Description")
#Html.ValidationMessageFor(model => model.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.IsRight)
</div>
<div class="editor-field">
#Html.DropDownList("IsRight", String.Empty)
#Html.ValidationMessageFor(model => model.IsRight)
</div>
</li>
<li> <div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.TextBox("answer[1].Description")
#Html.ValidationMessageFor(model => model.Description)
</div> <div class="editor-label">
#Html.LabelFor(model => model.IsRight)
</div>
<div class="editor-field">
#Html.DropDownList("IsRight", String.Empty)
#Html.ValidationMessageFor(model => model.IsRight)
</div> </li>
<li> <div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.TextBox("answer[2].Description")
#Html.ValidationMessageFor(model => model.Description)
</div> <div class="editor-label">
#Html.LabelFor(model => model.IsRight)
</div>
<div class="editor-field">
#Html.DropDownList("IsRight", String.Empty)
#Html.ValidationMessageFor(model => model.IsRight)
</div> </li>
<li> <div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.TextBox("answer[3].Description")
#Html.ValidationMessageFor(model => model.Description)
</div> <div class="editor-label">
#Html.LabelFor(model => model.IsRight)
</div>
<div class="editor-field">
#Html.DropDownList("IsRight", String.Empty)
#Html.ValidationMessageFor(model => model.IsRight)
</div> </li>
<ol>
</fieldset>
<input type= "hidden" name = "questionid" value = #ViewBag.questionid>
<input type= "hidden" name = "assessmentid" value = #ViewBag.assessmentid>
<input type="submit" value="Add answer" />
</div>
}
</div>
and the following Post Ation Method:-
[HttpPost]
public ActionResult Create(int questionid, ICollection<Answer> answer)
{
if (ModelState.IsValid)
{
foreach (var a in answer){
repository.AddAnswer(a);
repository.Save();
}
return PartialView("_details2",answer);
}
return View("_details2",answer);}
and last thing the _details2 partial view which contains the newly added objects:-
#model IEnumerable<Elearning.Models.Answer>
#{
ViewBag.Title = "Details";
}
#foreach (var m in Model)
{
<tr id = #m.AnswersID>
<td>
#Html.DisplayFor(modelItem => m.Description)
</td>
<td>
#*#Html.DisplayFor(modelItem => Model.Answer_Description.description)*#
#ViewBag.Answerdesription
</td>
<td>
#Ajax.ActionLink("Delete", "Delete", "Answer",
new { id = m.AnswersID },
new AjaxOptions
{
Confirm = "Are You sure You want to delete this Answer ?",
HttpMethod = "Post",
UpdateTargetId = #m.AnswersID.ToString(),
OnSuccess = "removePartial2"
})
</td>
</tr>
}
but the above is not working nethier the objects will be added nor the partial view will be returned , so how i can solve this issue???
BR
You bind your view to a single Elearning.Models.Answer object, how are you expecting to get a collection of Answers as a parameter in your Action? The default model binder will try to bind your view fields to the parameter in the Action but it won't be able to as it's a collection.
What you could try to do is to bind your View to a List<Elearning.Models.Answer> and feed it 4 empty Answer objects, then you can create a strongly typed Partial view that expects one Elearning.Models.Answer, add the Partial in a foreach and, when posting the form, expect that the default model binder does it work and fill your action method with a brand new List of Answer objects.
As an alternative, you can create a View Model object that contains the fields in your View, including those 4 description fields. You add them as Html.TextboxFor to bind each of them to a different property in the View Model. Then you can collect them in your action, provided you change it to public ActionResult Create(int questionid, ViewModelAnswer answer)
Does it make sense?
Your model should contain a list and code like this:
#for (int i=0; i < Model.FavouriteMovies.Count; i++) {
#Html.LabelFor(model => model.YourList[i].Field)
#Html.EditorFor(model => model.YourList[i].Field)
#Html.ValidationMessageFor(model => model.YourList[i].Field)
}
which will print something like:
<label for="YourList_0__Field">Field Name</label>
The Field Name field is required.
And receive the model back in your controller:
public ActionResult MyAction(MyModel model)
{
// First element?
model.YourList[0].
}

Categories