I'm having troubles with my form validation. For some reason the form ignores any [Required] attribute I have in my model.
Model: (Stripped down for easy read)
public class NotEmployeesModel
{
public NotEmployeesModel()
{
DetailModel = new NotEmployeesDetailModel();
}
public NotEmployeesDetailModel DetailModel { get; set; }
}
public class NotEmployeesDetailModel
{
public NotEmployeesDetailModel()
{
}
public NotEmployeesDocumentModel DocumentModel { get; set; }
}
public class NotEmployeesDocumentModel
{
public NotEmployeesDocumentModel()
{
}
public NotEmployeeDocumentInputModel DocumentInput { get; set; }
public class NotEmployeeDocumentInputModel
{
public NotEmployeeDocumentInputModel()
{
}
public NotEmployeeDocumentInputModel(int notEmployeeId)
{
NotEmployeeId = notEmployeeId;
}
public int NotEmployeeId { get; set; }
public int SelectedDocumentType { get; set; }
[Required(ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "Star")]
public string Description { get; set; }
[Required(ErrorMessageResourceType = typeof(ErrorMessages), ErrorMessageResourceName = "Star")]
public HttpPostedFileBase File { get; set; }
}
}
For each view or partial I have a separate model class.
Form:
#model NotEmployeesDocumentModel
#using (Html.BeginForm("AddNotEmployeeDocument", "Home", FormMethod.Post, new { id = "form-add-document", enctype = "multipart/form-data" }))
{
#Html.HiddenFor(x => x.DocumentInput.NotEmployeeId)
<table class="table-output">
<thead>
<tr>
<td>#Html.Label("Sort", Labels.Sort)</td>
<td>#Html.Label("Description", Labels.Description)</td>
<td>#Html.Label("Type", Labels.Type)</td>
<td class="text-align-right"></td>
</tr>
</thead>
<tbody>
<tr>
<td>
<i class="fa fa-plus-square cursor-pointer add-document"></i>
<input type="file" id="DocumentInput_File" name="DocumentInput.File" required="required" />
#Html.ValidationMessageFor(x => x.DocumentInput.File)
</td>
<td>
#Html.TextBoxFor(x => x.DocumentInput.Description, new { #class = "width300px hardware-setup-input", placeholder = "Vul hier een omschrijving in..." })
#Html.ValidationMessageFor(x => x.DocumentInput.Description)
</td>
<td>
#Html.DropDownListFor(x => x.DocumentInput.SelectedDocumentType, Model.DocumentTypes, "--- Maak een keuze ---")
</td>
<td class="text-align-right">
<span id="btn-add-document" class="button-org">#Labels.Save</span>
</td>
</tr>
</tbody>
</table>
}
Structure of my page: View / Partial / Partial (Here is my form)
JS:
$("#btn-add-document").on("click", function () {
var frm = $("#form-add-document");
if (frm.valid()) {
frm.ajaxSubmit({
dataType: "html",
success: function (responseText) {
$("#document-container").html(responseText);
$("#DocumentInput_Description").text("");
$("#DocumentInput_SelectedDocumentType").val("");
loadDocumentPartial();
}
});
}
});
I'm using jQuery Form plugin from malsup to submit my form through ajax.
Controller:
[HttpPost]
public ActionResult AddNotEmployeeDocument(NotEmployeesDocumentModel input)
{
// do some code
}
As you can see in the parameters of my ActionResult I can't put NotEmployeesDocumentInputModel like I always do but I'm forced to use the parent class.
I have no idea what I'm doing wrong. This is the first time I encounter such problem.
Related
I have one table which contains static data with 5 columns and 4 rows. I have another table which would accept input based on static table. I am trying to build a view with table where all field from static Db table is displayed and few text box so that user could fill in the data. But when I click on submit button no data is saved in second table. Can anyone help me with this. How could I submit multiple rows to database based on static database table.
Following are the codes:
View Model:
public class DBM2BillingViewModel
{
public List<DatabaseM2> databaseM2List { get; set; }
[Display(Name = "Items")]
public string Name { get; set; }
[Display(Name = "PO Item No.")]
public int POItemNo { get; set; }
[Display(Name = "Date of Effect")]
public DateTime DateOfEffect { get; set; }
[Display(Name = "Baseline Volume")]
public float baselineVolume { get; set; }
[Display(Name = "Billing Month")]
public string BillingMonth { get; set; }
[Display(Name = "Monthly Device Count")]
public float DeviceCount { get; set; }
}
**Controller**
//Get Method
public IActionResult Create()
{
DBM2BillingViewModel dbm2billingVM = new DBM2BillingViewModel();
dbm2billingVM.databaseM2List = _context.databaseM2s.ToList();
return View(dbm2billingVM);
}
//Post Method
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(DBM2BillingViewModel model)
{
foreach(var dbm2 in model.databaseM2List)
{
DeviceBilling addModel = new DeviceBilling();
addModel.Name = dbm2.Name;
addModel.TrackName = "Database M2";
addModel.POItemNo = dbm2.POItemNo;
addModel.DateOfEffect = dbm2.DateOfEffect;
addModel.baselineVolume = dbm2.BaselineVolume;
addModel.BillingMonth = model.BillingMonth;
addModel.DeviceCount = model.DeviceCount;
_context.deviceBillings.Add(addModel);
}
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
enter code here
View
#model FinancantPro.Models.DeviceCountModel.DBM2BillingViewModel
#{
ViewData["Title"] = "Create";
}
<div class="container">
<table class="table table-striped border">
<thead>
<tr class="table-info">
<th>#Html.DisplayName("Item Name")</th>
<th>#Html.DisplayName("PO Item No#")</th>
<th>#Html.DisplayName("Date Of Effect")</th>
<th>#Html.DisplayName("Baseline Volume")</th>
<th>#Html.DisplayName("Billing Month")</th>
<th>#Html.DisplayName("Device Count")</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.databaseM2List.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(d => d.databaseM2List[i].Id)
#Html.DisplayFor(d => d.databaseM2List[i].Name)
</td>
<td>
#Html.DisplayFor(d => d.databaseM2List[i].POItemNo)
</td>
<td>
#Html.DisplayFor(d => d.databaseM2List[i].DateOfEffect)
</td>
<td>
#Html.DisplayFor(d => d.databaseM2List[i].BaselineVolume)
</td>
<td>
#Html.TextBoxFor(d => d.BillingMonth, new { #class = "form-control" })
</td>
<td>
#Html.TextBoxFor(d => d.DeviceCount, new { #class = "form-control" })
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Create" />
</div>
</div>
**************************
If I add list in View I am not able to resolve the Model
I'm new to MVC and feel like I'm missing something conceptually. I'm trying to create a page where I can submit multiple records at once, dynamically adding and removing records via ajax
I've been trying to follow examples like Steven Sanderson and haacked but whenever I submit the page the collection contains 0 records.
Looking at Fiddler I can see both rows being submitted yet something goes missing.
I've seen references to editor templates and html helpers to make this prettier but these have mostly gone over my head and most of the examples I've found are in old versions of MVC as well.
Model
public class Projection
{
public int ID { get; set; }
[Display(Name = "Project")]
public int ProjectId{ get; set; }
[Display(Name = "Employee")]
public int EmployeeId { get; set; }
public decimal Hours { get; set; }
[DisplayFormat(DataFormatString = "{0:MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime Month { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public virtual Project Project { get; set; }
public virtual Employee Employee { get; set; }
}
Controller
// GET: Projections/CreateMulti
[HttpGet]
public IActionResult CreateMulti()
{
ViewBag.Index = 0;
ViewData["EmployeeId"] = new SelectList(_context.Employees.Where(e => e.Active).OrderBy(e => e.FullName), "ID", "FullName");
PopulateClientsDropDownList();
PopulateProjectsDropDownList();
return View(new Projection { Month = DateTime.Today.AddMonths(1).AddDays(1 - DateTime.Today.Day) });
}
[HttpGet]
public IActionResult GetNewRow(int index)
{
ViewBag.Index = index;
ViewData["EmployeeId"] = new SelectList(_context.Employees.Where(e => e.Active).OrderBy(e => e.FullName), "ID", "FullName");
PopulateClientsDropDownList();
PopulateProjectsDropDownList();
return PartialView("CreateMultiPartial", new Projection { Month = DateTime.Today.AddMonths(1).AddDays(1 - DateTime.Today.Day) });
}
// POST: Projections/CreateMulti
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult CreateMulti(ICollection<Projection> ProjectionList)
{
foreach (Projection item in ProjectionList)
{
//do something here
}
return View(ProjectionList);
}
View
#model Test.Models.Projection
#{int Index = 1;
string newRowUrl = #Url.Action("GetNewRow", "Projections", new { index = #Index });}
<form asp-action="CreateMulti">
<table class="table" id="myTable">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Project.Client)
</th>
<th>
#Html.DisplayNameFor(model => model.Project)
</th>
<th>
#Html.DisplayNameFor(model => model.Employee)
</th>
<th>
#Html.DisplayNameFor(model => model.Month)
</th>
<th>
#Html.DisplayNameFor(model => model.Hours)
</th>
<th></th>
</tr>
</thead>
<tbody>
#{Html.RenderPartial("CreateMultiPartial", Model);}
</tbody>
</table>
<div>Add Another Row</div>
<div>
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$("#addNew").click(function () {
event.preventDefault();
$.ajax({
url: $("#addNew").attr("href"),
cache: false,
success: function (html) {
$('#myTable > tbody:last-child').append(html);
indexIterate();
}
});
});
var currentIndex = 1;
function indexIterate() {
var newHref = $("#addNew").attr("href");
var newerHref = newHref.replace(/(?:index=)[0-9]+/i, "index=" + ++currentIndex);
$("#addNew").attr("href", newerHref);
};
});
</script>
}
Fiddler
Projections.Index=0&ClientID=7&Projections%5B0%5D.ProjectId=40&Projections%5B0%5D.EmployeeId=3&Projections%5B0%5D.Month=10%2F2017&Projections%5B0%5D.Hours=1&Projections.Index=1&ClientID=15&Projections%5B1%5D.ProjectId=19&Projections%5B1%5D.EmployeeId=32&Projections%5B1%5D.Month=10%2F2017&Projections%5B1%5D.Hours=1&__RequestVerificationToken=AAA
I have the following viewmodel structure:
ItemViewModel containing a property Model of type BaseViewModel.
In this concrete example, Model is of type WeekManagerWorkScheduleViewModel. This viewmodel in turns consists of an IEnumerable of type ManagerWorkScheduleViewModel.
Inside the ItemView.cshtml the following is used:
<form class="form-horizontal" id="#Model.FormName" name="#Model.FormName" onsubmit="#Model.OnSubmitFunction">
<div class="form-group form-group-sm">
#Html.EditorFor(model => model.Model, Model.ItemViewName + "View")
</div>
<button class="btn pull-left" type="submit" style="margin: 2px;">
<span class="glyphicon glyphicon-save"></span>
#Model.ViewConfiguration.SaveText #Model.ItemTitle
</button>
</form>
I have omitted some details (basically a bunch of if's determining whether or not to add CRUD buttons. The Model.ItemViewName is the typename (in this case its WeekManagerWorkScheduleViewModel).
public class WeekManagerWorkScheduleViewModel : BaseViewModel
{
[HiddenInput]
public int RegionId { get; set; }
[HiddenInput]
public IEnumerable<DateTime> Dates { get; set; }
public IEnumerable<ManagerWorkScheduleViewModel> WorkSchedules { get; set; }
}
The WeekManagerWorkScheduleView.cshtml looks like this:
#using DRSTransportPortal.ViewModels
#model WeekManagerWorkScheduleViewModel
#{
ViewBag.Title = "Ugentlig arbejdsplan - ledere";
}
#Html.HiddenFor(m => m.RegionId)
<table class="table">
<thead>
<tr>
<th><b>Leder</b></th>
<th><b>Uge</b></th>
<th><b>Mandag</b></th>
<th><b>Tirsdag</b></th>
<th><b>Onsdag</b></th>
<th><b>Torsdag</b></th>
<th><b>Fredag</b></th>
<th><b>Lørdag</b></th>
<th><b>Søndag</b></th>
</tr>
<tr>
<th></th>
<th></th>
#foreach (var date in Model.Dates)
{
<th><i><small>#date.Date.ToString("dd-MM-yyyy")</small></i></th>
}
</tr>
</thead>
<tbody>
#Html.EditorFor(m => m.WorkSchedules)
</tbody>
</table>
I have a view called ManagerWorkScheduleViewModel.cshtml residing in Views/imalazysod/EditorTemplates (MVC knows this location, as I am using a custom view engine, derived from Razor):
#using DRSTransportPortal.ViewModels
#model ManagerWorkScheduleViewModel
<tr id="#Model.Id">
<td>#Html.HiddenFor(m => m.Id)</td>
<td>#Html.DisplayFor(m => m.ManagerName, new { htmlAttributes = new { #class = "form-control editoritem" } })</td>
<td>#Html.DisplayFor(m => m.DateWeekText, new { htmlAttributes = new { #class = "form-control editoritem" } })</td>
<td>#Html.EditorFor(m => m.MondayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.TuesdayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.WednesdayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.ThursdayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.FridayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.SaturdayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.SundayCodeChoice, new { #class = "form-control editoritem" })</td>
</tr>
The "Choice" properties are all of type ChoiceViewModel using a ChoiceViewModel.cshtml.
Now, everything renders fine:
Screenshot (names omitted). Red box indicates 1 (one) nested viewmodel
The generated HTML looks like this (only the first row and first few cells are shown here):
<tr id="134">
<td><input name="Model.WorkSchedules[0].Id" id="Model_WorkSchedules_0__Id" type="hidden" value="134" data-val-required="Feltet Id skal udfyldes." data-val="true" data-val-number="The field Id must be a number."></td>
<td>OMITTED</td>
<td>2016 - 36</td>
<td>
<select name="Model.WorkSchedules[0].MondayCodeChoice.SelectedValue" class="form-control editoritem dropdown" id="SelectChoices" onchange="">
<option value="1">06:00 - 14:00</option>
<option value="19">06:00 - 18:00</option>
<option value="31">08:00 - 16:00</option>
<option value="2">10:00 - 18:00</option>
<option value="32">10:00 - 18:00</option>
<option value="23">Bagvagt</option>
<option value="22">Ferie</option>
<option value="8">Fri</option>
<option value="3">Kontor</option>
<option value="15">Kussus</option>
<option value="16">Syg</option>
</select>
</td>
REST OF HTML IS OMMITTED (CONTINUES FOR 12 ROWS WITH 10 CELLS EACH)
</tr>
However, when I post back (using jQuery, ajax btw) this is what I get:
Controller breakpoint, after modelbinding
I have tried putting in a custom modelbinder on the BaseViewModel, and debugging that haven't seen anything that doesn't look alright. It resolves the correct types (WeekManagerWorkScheduleViewModel, etc.).
Using that, my Forms element from ControllerContext->...->Request is:
{Model.Id=141&ModelType=DRSTransportPortal.ViewModels.WeekManagerWorkScheduleViewModel%2c+DRSTransportPortal%2c+Version%3d1.0.0.0%2c+Culture%3dneutral%2c+PublicKeyToken%3dnull&Model.RegionId=1&Model.WorkSchedules%5b0%5d.Id=134&Model.WorkSchedules%5b0%5d.MondayCodeChoice.SelectedValue=22&Model.WorkSchedules%5b0%5d.TuesdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.WednesdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.ThursdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.FridayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.SaturdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.SundayCodeChoice.SelectedValue=1&..... CONTINUES FOR ALL 12 NESTED VIEWMODELS}
I have made sure the following is true:
All bindings are made to properties (default ie. {get;set;})
No viewmodels, including BaseViewModel, have constructors (ie. default constructors should be created)
All properties and classes are public
So...my question is, why is the modelbinder unable to create the viewmodels of the list? I have no problem using the template elsewhere, editing pr. person, ie. one row at a time).
Yes, I know there are MANY questions and MANY answers related to MVC modelbinding, but none seem to quite fall into this category (same goes for books). What really puzzles me, is that it is recognized that a list of 12 items is needed, but it just isn't populated.
EDIT:
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
var type = Type.GetType((string) typeValue.ConvertTo(typeof (string)), true);
if(!typeof(BaseViewModel).IsAssignableFrom(type))
throw new InvalidOperationException("NOT A BASEVIEWMODEL");
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
[ModelBinder(typeof(BaseViewModelBinder))]
public class BaseViewModel
{
[HiddenInput]
public int Id { get; set; }
[HiddenInput]
public DateTime Created { get; set; }
[HiddenInput]
public DateTime Edited { get; set; }
public virtual string SelectionName { get; set; }
}
EDIT 2:
[HttpPost]
public override async Task<JsonResult> Save(ItemViewModel item, string parentName, int? parentId)
{
if (item?.Model == null)
{
const int result = 0;
return Json(new { result });
}
var model = item.Model as WeekManagerWorkScheduleViewModel;
if (model == null)
{
const int result = 0;
return Json(new { result });
}
foreach (var inner in model.WorkSchedules)
{
}
return await base.Save(item, parentName, parentId);
}
public class ManagerWorkScheduleViewModel : BaseViewModel
{
[HiddenInput]
public int? ManagerId { get; set; }
[DisplayName("Leder")]
public string ManagerName { get; set; }
[HiddenInput]
public int? DateWeekId { get; set; }
[DisplayName("År / Uge")]
public string DateWeekText { get; set; }
[HiddenInput]
public int? MondayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Mandag")]
public ChoiceViewModel MondayCodeChoice { get; set; }
[HiddenInput]
public int? TuesdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Tirsdag")]
public ChoiceViewModel TuesdayCodeChoice { get; set; }
[HiddenInput]
public int? WednesdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Onsdag")]
public ChoiceViewModel WednesdayCodeChoice { get; set; }
[HiddenInput]
public int? ThursdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Torsdag")]
public ChoiceViewModel ThursdayCodeChoice { get; set; }
[HiddenInput]
public int? FridayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Fredag")]
public ChoiceViewModel FridayCodeChoice { get; set; }
[HiddenInput]
public int? SaturdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Lørdag")]
public ChoiceViewModel SaturdayCodeChoice { get; set; }
[HiddenInput]
public int? SundayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Søndag")]
public ChoiceViewModel SundayCodeChoice { get; set; }
}
Ah, stupid me.
I forgot how I actually made this framework to begin with.
I created this mainly for CRUD operations, so I only need to define viewmodel (derived from BaseViewModel), view, repository and dto types for each business entity as well as an empty controller. Then everything else is using a standard set of controllers (three levels of derivation, depending on functionality needed) and all I need to do is create a config entry for the business entity, defining the vm, view, repo types as well as some CRUD options for buttons and labels.
All top level viewmodels and view are the same, like ItemView, ItemViewModel, ItemListView, SingleSelector, MultipleSelector etc etc.
What I had FORGOTTEN was that I limited myself to one business entity at a time, hence outputting the class (see the modelbinder), so I COULD use derived viewmodels...this works great, except obviously in this case.
What happens is, as the binding tries to populate the list, it keeps trying to populate it using the first viewmodel type (as this is the only one known after POST) and not the derived. DAMMIT! So back to the drawing table (no nesting :( )
I am trying to display a form for merging two contacts in ASP.net MVC 5.
The form should look like this where each row holds a radio button group consisting of 2 options:
The form shows up just fine, and the radio groups work (I can select each group). However, when the model is posted back to the server, the Values list is empty (not preserved). I would like to get both the selected id but of course also the actual text value back in the controller. I prefer to do this using Editor Templates and if possible without for loops.
The current results (Values = null):
EDIT to respond to comments: I would prefer not to re-fetch the values in the controller again, because it results in a call to a web service. I have tried some variants of HiddenFor without results.
My models look like this:
public class Contact
{
public List<ContactRow> Rows { get; set; }
public Contact()
{
this.Rows = new List<ContactRow>();
this.Rows.Add(new ContactRow("First Name", "Homer", "Homie"));
this.Rows.Add(new ContactRow("Last Name", "Simpson", "Simson"));
this.Rows.Add(new ContactRow("Email", "mail1", "mail2"));
this.Rows.Add(new ContactRow("Company Phone", "Phone1", "Phone2"));
this.Rows.Add(new ContactRow("Mobile Phone", "Mobile1", "Mobile2"));
}
}
public class ContactRow
{
public int Selection { get; set; }
public string Label { get; set; }
public List<ValueSet> Values { get; set; }
public ContactRow(string Label, string LeftValue, string RightValue, int Selection = 0)
{
if (LeftValue== null) LeftValue= "";
if (RightValue== null) RightValue= "";
this.Label = Label;
this.Selection = Selection;
this.Values = new List<ValueSet>(2);
this.Values.Add(new ValueSet() { ID = 0, ValueText = LeftValue});
this.Values.Add(new ValueSet() { ID = 1, ValueText = RightValue});
}
public ContactRow() { }
}
public class ValueSet
{
public int ID { set; get; }
public string ValueText { set; get; }
}
The Controller:
public ActionResult Index()
{
Contact model = new Contact();
return View(model);
}
public ActionResult MergeContacts(Contact model)
{
return RedirectToAction("Index");
}
And the views:
Index.cshtml
#model RadioTest.Models.Contact
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm("MergeContacts", "Home", FormMethod.Post, new { encType = "multipart/form-data", id = "contactDetailsForm", name = "contactDetailsForm" }))
{
<div>
<table class="table" width="100%">
<tr>
<th style="text-align:left">Label</th>
#*<th style="text-align:right">R1</th>*#
<th style="text-align:left">
Left Value
</th>
#*<th style="text-align:right">R2</th>*#
<th style="text-align:left">
Right Value
</th>
</tr>
#Html.EditorFor(model => model.Rows)
</table>
<input type="submit" />
</div>
}
Editor Template for ContactRow:
ContactRow.cshtml
#model RadioTest.Models.ContactRow
<tr>
<td style="text-align:left">
#Html.DisplayFor(model => model.Label)
</td>
#foreach (var v in Model.Values)
{
<td style="text-align:left">
#Html.RadioButtonFor(model => model.Selection, v.ID) #v.ValueText
</td>
}
</tr>
#Html.HiddenFor(model => model.Label)
Just change your foreach to for:
#model MVCApp.Controllers.ContactRow
<tr>
<td style="text-align:left">
#Html.DisplayFor(model => model.Label)
</td>
#for (int i = 0; i < Model.Values.Count; i++)
{
<td style="text-align:left">
#Html.RadioButtonFor(model => model.Selection, Model.Values[i].ID) #Model.Values[i].ValueText
#Html.HiddenFor(model => Model.Values[i].ID)
#Html.HiddenFor(model => Model.Values[i].ValueText)
</td>
}
</tr>
#Html.HiddenFor(model => model.Label)
I have a controller that looks like below:
public async Task<ActionResult> CreateProduct([Bind(Include = "Id,MyCollection")] MyClass myClass)
and this is the view:
<div class="form-group">
#Html.LabelFor(model => model.Id, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Id)
#Html.ValidationMessageFor(model => model.Id)
</div>
</div>
<table>
<tr>
<th>#Html.LabelFor(model => model.MyCollection.First().A)</th>
<th>#Html.LabelFor(model => model.MyCollection.First().B)</th>
<th>#Html.LabelFor(model => model.MyCollection.First().C)</th>
</tr>
#foreach (var item in this.Model.Warnings)
{
<tr>
<td>#Html.ValueFor(model => item.A)</td>
<td>#Html.EditorFor(model => item.B)</td>
<td>#Html.EditorFor(model => item.C)</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
when I click Save, it posts to the action but only Id is assigned to the object, not myCollection.
What do I need to do to include the collection when posting them to controller?
Update
Model is generated by Entity Framework
public abstract partial class MyBaseClass
{
public Module()
{
this.MyCollection= new HashSet<Warning>();
}
public int Id { get; set; }
public virtual ICollection<Warning> MyCollection { get; set; }
}
public partial class MyClass : MyBaseClass
{
// more properties that aren't used on this controller
}
In order to get this to work you need to understand how ASP.NET MVC handles model binding for a List. Scott Hansleman has a good post explaining this.
Since your question was rather vague in terms of what the actual properties you're dealing with are, I put together a little example which successfully binds to a list:
Controller
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
//Initial test data
var zoo = new Zoo()
{
Id = 1,
Name = "Vilas Zoo",
Animals = new List<Animal>()
{
new Animal() {
Id = 1,
Name = "Red Panda"
},
new Animal() {
Id = 2,
Name = "Sloth"
},
new Animal() {
Id = 3,
Name = "Badger"
},
}
};
return View(zoo);
}
[HttpPost]
public JsonResult Index(Zoo zoo)
{
return Json(zoo);
}
}
Model
public class Zoo
{
public int Id { get; set; }
public string Name { get; set; }
public List<Animal> Animals { get; set; }
}
public class Animal
{
public int Id { get; set; }
public string Name { get; set; }
}
View
<h1>#Model.Id - #Model.Name</h1>
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
for (var i=0; i < Model.Animals.Count; i++)
{
#Html.EditorFor(m => Model.Animals[i])
}
<button type="submit">Save it, yo</button>
}
Notice how I'm using a for loop instead of a foreach and the actual index of the loop is being used in the call to EditorFor.