View not sending ViewModel to Controller - asp.net-mvc

I am attempting to send a ViewModel with a IList<Hole> from the view to the controller after data is gathered in a for loop to pass into a method, however, the ViewModel being passed continues to be null. What am I missing that is not passing the ViewModel from the View to the Controller?
My ViewModel is:
public class HoleViewModel : IEnumerable
{
public int FacilityId { get; set; }
public int CourseId { get; set; }
//public Hole Hole { get; set; }
public IList<Hole> Holes { get; set; }
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
My View is:
#using GT_App.Models
#model GT_App.ViewModel.HoleViewModel
....
<form method="post" action="/Hole/Create">
<fieldset>
<div>
#{
var holeCount = 4;
}
<table style="display: inline">
<thead>
<th>Number</th>
<th>Yardage</th>
<th>Par</th>
<th>Hdcp</th>
</thead>
#for (int i = 0; i < holeCount; i++)
{
<tr>
<td>
#Html.TextBoxFor(m => m.Holes[i].Number)
</td>
<td>
#Html.TextBoxFor(m => m.Holes[i].Yardage)
</td>
<td>
#Html.TextBoxFor(model => model.Holes[i].Par)
</td>
<td>
#Html.TextBoxFor(model => model.Holes[i].Handicap)
</td>
</tr>
}
</table>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
</form>
My Controller is:
public ActionResult Create()
{
ViewBag.FacilityId = new SelectList(db.Facilities, "FacilityId", "Name");
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "Name");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(HoleViewModel holes)
{
if (ModelState.IsValid)
{
for (int i = 0; i < holes.Holes.Count; i++)
{
var item = new Hole();
if (Session["FacilityId"] != null || Convert.ToInt32(Session["FacilityId"]) != 0)
{
item.FacilityId = Convert.ToInt32(Session["FacilityId"]);
}
if (Session["CourseId"] != null || Convert.ToInt32(Session["CourseId"]) != 0)
{
item.CourseId = Convert.ToInt32(Session["CourseId"]);
}
item.Number = Convert.ToInt32(Request.Form["Number" + i]);
item.Yardage = Convert.ToInt32(Request.Form["Yardage" + i]);
item.Par = Convert.ToInt32(Request.Form["Par" + i]);
item.Handicap = Convert.ToInt32(Request.Form["Handicap" + i]);
holes.Holes.Add(item);
}
// itterate thru collection to add individual holes to Entity
foreach (Hole hole in holes)
{
db.Holes.Add(hole);
db.SaveChanges();
}
//return RedirectToAction("Index");
}
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "Name", Session["CourseId"]);
//return View(Session["CourseId"]);
return RedirectToAction("Index");
}

If there's a model validation error in your second Create action, then typically you'd return the view again using the submitted model, which would then show the validation errors on the webpage.
You're not doing that - you're redirecting to the Index action regardless of whether the model was valid. I'd put the RedirectToAction call just after the call to SaveChanges, and then at the end of the method return View(holes);.
Oh, and I wouldn't put the SaveChanges call inside the loop. Do it after the loop. There are other issues with that code, but I'm going to stop there... :-)

Related

send a ViewModel which contains a list with a Html.BeginForm (MVC 4)

My viewmodel contains a integer list, the problem I have is that when I send my modified form viewmodel, it is always equal to null.
My ViewModel :
public class testViewModel
{
public List<int> itemTest { get; set;
}
Action in my Controller :
For example, I'll try to sum ​​the new values ​​entered into the form, but the sum calculated is always equal to 0, nothing changes.
public ActionResult form(int nbre)
{
testViewModel montest = new testViewModel()
{
itemTest = new List<int>()
};
for(int i=0;i<nbre ;i++)
{
montest.itemTest.Add(0);
}
return View(montest);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult form(testViewModel maListe)
{
int somme = 0;
if (maListe.itemTest != null)
{
if (maListe.itemTest.Count() != 0)
{
foreach (var item in maListe.itemTest)
{
somme += item;
}
}
}
//listtest = maListe;
return RedirectToAction("test2", new { qte = somme });
}
My view
#model MvcWebRole1.ViewModels.testViewModel
#{
ViewBag.Title = "Formulaire";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<table>
#foreach (var item in Model.itemTest)
{
<tr >
<td >
#Html.Label("Quantitée")
</td>
<td>
#Html.EditorFor(model => item)
#Html.ValidationMessageFor(model => item)
</td>
</tr>
}
</table>
<input type="submit" value="Valider" />
}
Thank you kindly help me
You need to index each item in your collection. The issue with your code seems to be the use of foreach. You really want to use for instead and pass in the index with the EditorFor call.
for (int i = 0; i < Model.Items.Count; i++) {
#Html.EditorFor(m => m.Items[i])
}
This only works for ordered lists that will never change their order. If you want to reorder items I suggest your read Phil Haack's great post on sending lists to the server.
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
list binding
<form>
#for(int i=0;i<Model.itemTest.Count ;i++)
{
#Html.TextBoxFor(x=>x.itemTest[i])
//or just <input type="text" name="itemTest "/> working to
}
for(int i=0;i<nbre ;i++)
{
montest.itemTest.Add(0);
}
return View(montest);
Looks like you are filling your int array with zeroes instead of i's. This should read montest.itemTest.Add(i); .

show data from viewmodel in view

I tried this code but I have error like this:
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[XNet.Repository.Model.RoomType]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable`1[XNet.Repository.Model.EditRoomTypeViewModel]'.
I don't know, whats part give an error. Please help.
my service
public List<EditRoomTypeViewModel> GetViewRoom(int RoomTypeID)
{
List<RoomType> roomTypes = (from d in _RoomTypeRepository.All()
select d).ToList();
List<EditRoomTypeViewModel> editRoomTypeViewModel = new List<EditRoomTypeViewModel>();
foreach (RoomType roomType in roomTypes)
{
editRoomTypeViewModel.Add(new EditRoomTypeViewModel
{
RoomTypeID = RoomTypeID,
RoomTypeName = roomType.RoomtypeName,
RoomTypeDescription = roomType.RoomTypeDescripton,
});
}
return editRoomTypeViewModel;
}
my controller
public ActionResult Room()
{
ViewBag.hotel = _hotelService.GetByID(2).HotelName;
List<EditRoomTypeViewModel> editRoomTypeViewModel = _roomViewService.GetViewRoom(_HotelID);
return View(editRoomTypeViewModel.FirstOrDefault());
}
my view model
public class EditRoomTypeViewModel
{
public int RoomTypeID { get; set; }
public string RoomTypeName { get; set; }
public string RoomTypeDescription { get; set; }
}
my view
#model IEnumerable<XNet.Repository.Model.EditRoomTypeViewModel>
#{
ViewBag.Title = "Room";
}
<h2>Room</h2>
<div>
#Html.Label("Hotel Name");
</div>
<div>
#ViewBag.hotel
</div>
<table>
#foreach (var a in Model)
{
<tr>
<td>
#Html.DisplayFor(model => a.RoomTypeName)
</td>
<td>
<input style="width:100px;" type="button" title="EditRoomType" value="Edit" onclick="location.href='#Url.Action("EditRoom", "Hotel", new { RoomTypeID = a.RoomTypeID})'" />
</td>
</tr>
}
</table>
<input style="width:200px;" type="button" title="EditRoomType" value="New Room Type" onclick="location.href='#Url.Action("NewRoom", "Hotel") '" />
I noticed that you returned just one editRoomTypeViewModel object in your controller, but in your view you declared the model as IEnumerable<XNet.Repository.Model.EditRoomTypeViewModel>.
Another point is that the error seems to be related to an assignment of ViewBag somewhere else, cause it contains thisdictionaryrequires a model item of type and probablt the only thing that is of type dictionary is ViewBag.
Just remove the .FirstOrDefault() in the controller action and you should be good to go.
public ActionResult Room()
{
ViewBag.hotel = _hotelService.GetByID(2).HotelName;
List<EditRoomTypeViewModel> editRoomTypeViewModel = _roomViewService.GetViewRoom(_HotelID);
return View(editRoomTypeViewModel);
}

MVC trying to pass model from razor view to controller

So my story is that I am having trouble with the post to the controller, the view seems to work fine. When the postback happens the tm.BookId is 0 (should be 1) and the list count is 0. First I will display the model:
public class TransferModel
{
public TransferModel()
{
cbItems = new List<CheckBoxItem>();
}
public List<CheckBoxItem> cbItems {get;set;}
public int BookId;
public class CheckBoxItem
{
public int AttributeId { get; set; }
public string Attribute { get; set; }
public bool Selected { get; set; }
}
}
The Controller part:
public ActionResult AddAttributes(int id = 0)
{
db.transMod.BookId = id;
BookInfo book = db.BookInfoes.Find(id);
var latts = db.BookAtts.ToList();
foreach (BookAtt ba in latts)
{
db.transMod.cbItems.Add(new TransferModel.CheckBoxItem { Attribute = ba.Attribute, AttributeId = ba.BookAttId, Selected = false });
}
List<BookAtt> atInList = book.BookAtts.ToList();
foreach (TransferModel.CheckBoxItem cb in db.transMod.cbItems)
{
if (atInList.Exists(item => item.Attribute == cb.Attribute))
cb.Selected = true;
}
return View(db.transMod);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddAttributes(TransferModel tm)
{
List<BookAtt> atPool = db.BookAtts.ToList();
BookInfo book = db.BookInfoes.Find(tm.BookId);
foreach (TransferModel.CheckBoxItem sel in tm.cbItems)
{
if (sel.Selected)
book.BookAtts.Add(atPool.Find(item1 => item1.Attribute == sel.Attribute));
}
db.SaveChanges();
return RedirectToAction("AddAttributes");
}`enter code here`
And finally the view:
#model BrightStar.Models.TransferModel
#{
ViewBag.Title = "Update Attributes";
}
<h2>Add Attributes</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
<table>
#Html.HiddenFor(model => Model.BookId)
#Html.HiddenFor(model => Model.cbItems)
#foreach (var itm in Model.cbItems)
{
<tr>
<td>#Html.HiddenFor(mo => itm.AttributeId)</td>
<td>#Html.CheckBoxFor(mo => itm.Selected)</td>
<td>#Html.DisplayFor(mo => itm.Attribute)</td>
</tr>
}
</table>
<p>
<input type="submit" value="Save" />
</p>
}
enter code here
Model binding doesn't happen automatically, items needs to be in certain format to get binded to list properties in POST actions. Check this out.
Try checking out the value of BookId property in the DOM to confirm it is 1, otherwise it should bind normally.
You should reference your model's properties in helpers to correctly generate names for your controls:
#Html.HiddenFor(model => Model.cbItems)
should be
#Html.HiddenFor(model => model.cbItems)

Values not being posted from Partial View MVC4

For some reason my partial view is always returning null values to the controller. I've tried implementing this in several different ways (including editor templates) but here's my latest.
Basically I'm rendering a view with a jquery ui datepicker. When the user picks a date I then render my partial view. The partial view is being rendered correctly but when I submit the partial view it's returning a null object to the controller
View Model:
public class Attendance
{
public int RecordId { get; set; }
public int GroupId { get; set; }
public int MemberId { get; set; }
public string MemberName { get; set; }
public System.DateTime Date { get; set; }
public bool Present { get; set; }
}
Parent View:
#{
ViewBag.Title = "Take Attendance";
}
<h2>Take Attendance</h2>
#Html.Hidden("GroupId", (int)ViewBag.GroupId)
<div>
#Html.Label("Select A Date")
#Html.JQueryUI().Datepicker("attendanceDate",ViewBag.AttendanceDate)
.OnSelect("attendanceDate.onSelect")
</div>
<div id="attendanceRecords">
</div>
#section scripts
{
<script src="#Url.Content("../../Scripts/Views/TakeAttendance.js")" type="text/javascript"></script>
}
Partial View:
#model IEnumerable<AT.Manager.Models.Attendance>
#using (Html.BeginForm("TakeAttendance", "Groups")) {
#Html.ValidationSummary(true)
<table>
<tr>
<th>Name</th>
<th>Present?</th>
</tr>
#for (int i = 0; i < Model.Count(); i++ )
{
<tr>
<td>#Html.DisplayFor(model => Model.ElementAt(i).MemberName)</td>
<td>#Html.EditorFor(model => Model.ElementAt(i).Present)</td>
<td>#Html.HiddenFor(model => Model.ElementAt(i).GroupId)</td>
<td>#Html.HiddenFor(model => Model.ElementAt(i).MemberId)</td>
<td>#Html.HiddenFor(model => Model.ElementAt(i).RecordId)</td>
</tr>
}
</table>
<p>
<input type="submit" value="Create" />
</p>
}
Controller Action:
public ActionResult TakeAttendance(IEnumerable<Attendance> records)
{
if (ModelState.IsValid)
{
var newRecords = records.Select(x => new da.Attendance() { Id = x.RecordId, GroupId = x.GroupId, MemberId = x.MemberId, Date = x.Date });
AttendanceRepository attendanceRepo = new AttendanceRepository();
newRecords.Each(x => attendanceRepo.InsertOrUpdate(x));
return RedirectToAction("Index");
}
else
{
return View();
}
}
Any suggestions would be much appreciated.
You could try passing IList<Attendance> records to the view and then manipulate it like this:
for (int i = 0; i < Model.Count(); i++ )
{
<tr>
<td>#Html.DisplayFor(model => Model[i].MemberName)</td>
<td>#Html.CheckBoxFor(model => Model[i].Present)</td>
// ...
</tr>
}
Be sure to change #model IEnumerable<AT.Manager.Models.Attendance> to reflect the change of collection to IList.

MVC 4 Model is empty when I try to submit a form

I have two forms on one View executing two separate Action methods in one Controller.
The first form (frmRefresh) is responsible for getting data and displaying it on the form so the user can pick certain checkboxes. Once submitted, the data is returned just fine in the ViewModel and is properly displayed on the form. 11 records for the Templates and 3 records for the Guarantors are displyaed as checkboxes on the form.
The second form (frmProcess), is responsible for taking the data on the form (that came back from the first post above). The user makes selections on the screen and processes it against some logic in the Controller. I have List objects in the model and don't suppose I can use the FormCollection to process the data because of the complex objects. Basically, they are a collection of checkboxes. I really need to use the data that should be submitted in the Model because of processing in the Controller for that data.
When submitting the second form, I realize that the loanid & ddl will not be available unless I put them in a hidden field (because they are in a separate form) --- that's fine. What I'm having a great deal of difficulty in understanding is when I submit the second form (frmProcess), why doesn't the model view binder take the data from the form, put it in the model and submit it to my GeneratePDF action method.?
Number one, I really need some help in understanding why this is happening and number two, I really need a soltution which takes my model data from the form to the action method and processes it. As you can see in the Controller, at the end of the code, I'm enumerating the Templates in the ViewModel to process the data.
Please help, as I am totally stuck on this at work and they are depending on me for this. I just don't get why the model binder doesn't take the values on the form and submit it to the action method for processing. It appears I'm missing something to allow the data to get back into the Model upon submission.
Below is my pertinent code:
ViedwModel
public partial class ViewModelTemplate_Guarantors
{
public int SelectedTemplateId { get; set; }
public IEnumerable<PDFTemplate> Templates { get; set; }
public int SelectedGuarantorId { get; set; }
public IEnumerable<tGuarantor> Guarantors { get; set; }
public string LoanId { get; set; }
public string SelectedDeptText { get; set; }
public string SelectedDeptValue { get; set; }
public string LoanType { get; set; }
public bool ShowTemps { get; set; }
public string Error { get; set; }
public string ErrorT { get; set; }
public string ErrorG { get; set; }
public bool ShowGeneratePDFBtn { get; set; }
}
View
#model PDFConverterModel.ViewModels.ViewModelTemplate_Guarantors
#{
ViewBag.Title = "BHG :: PDF Generator";
}
<h2>#ViewBag.Message</h2>
<div>
<table style="width: 1000px">
<tr>
<td colspan="5">
<img alt="BHG Logo" src="~/Images/logo.gif" />
</td>
</tr>
#using (Html.BeginForm("Refresh", "Home", FormMethod.Post, new { id = "frmRefresh" })) { <tr>
<td>
#*#(Html.Kendo().NumericTextBox<int>()
.Name("txtLoanID")
.Placeholder("Enter numeric value")
)*#
#Html.LabelFor(model => model.LoanId)
#Html.TextBoxFor(model => model.LoanId)
#Html.ValidationMessageFor(model => model.LoanId)
</tr>
<tr>
<td>#Html.LabelFor(model => model.LoanType)
#Html.TextBox("SBA", "SBA")
#Html.ValidationMessageFor(model => model.LoanType)
#*#Html.TextBoxFor(model => model.LoanType)*#
</td>
<td>
<label for="ddlDept">Department:</label>
#(Html.Kendo().DropDownListFor(model => model.SelectedDeptText)
.Name("ddlDept")
.DataTextField("DepartmentName")
.DataValueField("DepartmentID")
.Events(e => e.Change("Refresh"))
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetDepartments", "Home");
});
})
)
#Html.ValidationMessageFor(model => model.SelectedDeptText)
</td>
</tr>
<tr>
<td colspan="3">
<input type="submit" id="btnRefresh" value='Refresh' />
</td>
</tr>
}
#using (Html.BeginForm("GeneratePDF", "Home", FormMethod.Post, new { id = "frmProcess" })) { if (Model.ShowGeneratePDFBtn == true)
{
if (Model.ErrorT != string.Empty)
{
<tr>
<td colspan="5">
<u><b>#Html.Label("Templates:")</b></u>
</td>
</tr>
<tr>
#foreach (var item in Model.Templates)
{
<td>
#Html.CheckBoxFor(model => item.IsChecked)
#Html.DisplayFor(model => item.TemplateName)
</td>
}
</tr>
}
else
{
Model.Error = Model.ErrorT;
}
if (Model.ErrorG != string.Empty)
{
<tr>
<td colspan="5">
<u><b>#Html.Label("Guarantors:")</b></u>
</td>
</tr>
<tr>
#foreach (var item in Model.Guarantors)
{
<td>
#Html.CheckBoxFor(model => item.isChecked)
#Html.DisplayFor(model => item.GuarantorFirstName) #Html.DisplayFor(model => item.GuarantorLastName)
</td>
}
</tr>
}
else
{
Model.Error = Model.ErrorG;
}
<tr>
<td>
<input type="submit" id="btnGeneratePDF" value='Generate PDF' />
</td>
</tr>
<tr>
<td colspan="5">
#Model.Error
</td>
</tr>
}
} </table>
</div>
<script type="text/javascript">
$('btnRefresh').on('click', '#btnRefresh', function () {
Refresh();
});
function Refresh() {
var LoanID = $("#LoanID").val();
if (LoanID != "") {
document.forms["frmTemps"].submit();
}
}
</script>
Controller
public ActionResult Index(ViewModelTemplate_Guarantors model)
{
ViewBag.Error = "";
model.ShowGeneratePDFBtn = false;
return View("Index", model);
}
// used for the first form "frmRefresh" [HttpPost] public ActionResult Refresh(ViewModelTemplate_Guarantors model) {
try
{
model.Error = string.Empty;
bool dbHasRows = db.ChkLoanFields(Convert.ToInt32(model.LoanId));
if (!dbHasRows)
{
model.ShowGeneratePDFBtn = false;
model.Error = "Details not available for this LoanId.";
return View("Index",model);
}
else
{
int TemplateCnt = 0;
int GuarantorCnt = 0;
//todo - modify 2nd & 3rd parms instead of hardcoding
ViewModelTemplate_Guarantors tg = db.SelectViewModelTemplate_Guarantors(Convert.ToInt32(model.LoanId), "All", "All", out TemplateCnt, out GuarantorCnt);
if (TemplateCnt > 0)
model.Templates = tg.Templates;
else
model.ErrorT = "Templates not available for this LoanType.";
if (GuarantorCnt > 0)
model.Guarantors = tg.Guarantors;
else
model.ErrorG = "Guarantors not available for this LoanId.";
model.ShowGeneratePDFBtn = true;
// right before the return here, the model is full of data. return View("Index", model); }
}
catch (Exception ex)
{
throw ex;
}
} [HttpPost] // when I check the data here (via submission from the "frmProcess" form, the model is completely empty, null, etc... WHY???? // i NEED the model data here to perform processing in this action method. public ActionResult GeneratePDF(ViewModelTemplate_Guarantors model) {
try
{
int FolderNo, GuarantorNum = 0;
string Folder, LoanFolder = String.Empty;
string FormId, FormName, GuarantorName = String.Empty;
int LoanId = Convert.ToInt32(model.LoanId);
LoanFolder = LoanId.ToString().PadLeft(8, '0');
//To calculate FolderId based on LoanId
if ((LoanId > 0) && (LoanId < 99000))
{
FolderNo = ((int)(LoanId / 10000) * 10000);
}
else
{
FolderNo = ((int)(LoanId / 1000) * 1000);
}
Folder = ((int)FolderNo).ToString();
Folder = Folder.PadLeft(8, '0');
//todo - 2nd parm SelectedValue of dept
List<sSRPTFundexDocCodes1_Test_Result> sSRPTFundexDocCodes1 = db.GetFormValues(Convert.ToInt32(model.LoanId), (model.SelectedDeptValue));
if (sSRPTFundexDocCodes1 != null)
{
foreach (PDFTemplate template in model.Templates) {
if (template.IsChecked == true) {
TemplateName not showing up in model after post.
This works fine... The values (the checkboxes and the corresponding names are displyaed on the form.
However, when posting the GeneratePDF button, all I see in the model is if the Checkbox is checked (which is great). After playing around with many of the following statements: (ValueFor, DisplayFor, LabelFor, EditorFor, etc), the value coming back for the Template name is blank. I need the name of the template that was checked in correspondance with the checkbox.
#Html.ValueFor(model => Model.Templates[i].TemplateName)
How can I accomplish this? Thanks ahead of time... Below is my updated code.
ViewModel public partial class ViewModelTemplate_Guarantors
{
public ViewModelTemplate_Guarantors()
{
Templates = new List<PDFTemplate>();
Guarantors = new List<tGuarantor>();
}
public int SelectedTemplateId { get; set; }
public List<PDFTemplate> Templates { get; set; }
public int SelectedGuarantorId { get; set; }
public List<tGuarantor> Guarantors { get; set; }
public string LoanId { get; set; }
public string SelectedDeptText { get; set; }
public string SelectedDeptValue { get; set; }
public string LoanType { get; set; }
public string Error { get; set; }
public string ErrorT { get; set; }
public string ErrorG { get; set; }
public bool ShowGeneratePDFBtn { get; set; }
}
Pertinet part of View:
if (Model.ShowGeneratePDFBtn == true)
{
if (Model.ErrorT == string.Empty)
{
<tr>
<td colspan="5">
<u><b>#Html.Label("Templates:")</b></u>
</td>
</tr>
<tr>
#for (int i = 0; i < Model.Templates.Count; i++)
{
<td>
#Html.CheckBoxFor(model => Model.Templates[i].IsChecked)
#Html.ValueFor(model => Model.Templates[i].TemplateName) </td>
}
</tr>
}
else
{
<tr>
<td>
<b>#Html.DisplayFor(model => Model.ErrorT)</b>
</td>
</tr>
}
if (Model.ErrorG == string.Empty)
{
<tr>
<td colspan="5">
<u><b>#Html.Label("Guarantors:")</b></u>
</td>
</tr>
<tr>
#for (int i = 0; i < Model.Guarantors.Count; i++)
{
<td>
#Html.CheckBoxFor(model => Model.Guarantors[i].isChecked)
#Html.ValueFor(model => Model.Guarantors[i].GuarantorFirstName) #Html.ValueFor(model => Model.Guarantors[i].GuarantorLastName) </td>
}
</tr>
}
else
{
<tr>
<td>
<b>#Html.DisplayFor(model => Model.ErrorG)</b>
</td>
</tr>
}
}
<tr>
<td colspan="3">
<input type="submit" name="submitbutton" id="btnRefresh" value='Refresh' />
</td>
#if (Model.ShowGeneratePDFBtn == true)
{
<td>
<input type="submit" name="submitbutton" id="btnGeneratePDF" value='Generate PDF' />
</td>
}
</tr>
<tr>
<td colspan="5">
#Model.Error
</td>
</tr>
Controller:
public ActionResult ProcessForm(string submitbutton, ViewModelTemplate_Guarantors model, FormCollection collection)
Basically, again it's working fine. When the form posts using the Generate PDF button, I get the checked value of each checkbox, but not the name of the template in the Model.
Am I missing something here???
The form before I submit is basically like below. It's the name of the checkbox (Form4) that I'm missing as a TemplateID in my Model once I get into the ActionResult.
public ActionResult ProcessForm(string submitbutton, ViewModelTemplate_Guarantors model, FormCollection collection)
checkbox (checked) Form4
#for (int i = 0; i < Model.Templates.Count; i++)
{
<td>
#Html.CheckBoxFor(model => Model.Templates[i].IsChecked)
#Html.DisplayFor(model => Model.Templates[i].TemplateName)
</td>
}
As I mentioned in my comment. The model binder cannot bind to an IEnumerable.
Your Model should look like this:
public partial class ViewModelTemplate_Guarantors
{
public ViewModelTemplate_Guarantors() {
Templates = new List<PDFTemplate>(); // These are important, the model binder
Guarantors = new List<tGuarantor>(); // will not instantiate nested classes
}
public int SelectedTemplateId { get; set; }
public List<PDFTemplate> Templates { get; set; }
public int SelectedGuarantorId { get; set; }
public List<tGuarantor> Guarantors { get; set; }
...
}
Further, your view should look like this:
...
#for(int i = 0; i < Model.Templates.Count; i++) // should really use label, not display
{
<td>
#Html.CheckBoxFor(model => Model.Templates[i].IsChecked)
#Html.DisplayFor(model => Model.Templates[i].TemplateName)
</td>
}
...
#for(int i = 0; i < Model.Guarantors.Count; i++)
{
<td>
#Html.CheckBoxFor(model => Model.Guarantors[i].isChecked)
#Html.DisplayFor(model => Model.Gurantors[i].GuarantorFirstName) #Html.DisplayFor(model => Model.Guarantors[i].GuarantorLastName)
</td>
}
...
Although a better choice would be to use an EditorTemplate and instead do this:
...
#Html.EditorFor(m => m.Templates)
...
#Html.EditorFor(m => m.Guarantors)
...
Then create a folder in ~/Views/Shared called EditorTemplates, and then create two files called Templates.cshtml and Guarantors.cshtml.
In those files you would do this:
#model PDFTemplate
<td>
#Html.CheckBoxFor(model => model.IsChecked)
#Html.DisplayFor(model => model.TemplateName)
</td>
and
#model Guarantors
<td>
#Html.CheckBoxFor(model => model.isChecked)
#Html.DisplayFor(model => model.GuarantorFirstName) #Html.DisplayFor(model => model.GuarantorLastName)
</td>
The editor templates will automatically iterate over the collection and will account for the correct naming format to make the model binder understand it's a collection.

Resources