How to configure list-based editor template binding for POST action?

I have a ApplicantBranchList model that is used as a property in a larger model as follows:

 [Display(Name = "Where would you want to work?")] public ApplicantBranchList PreferedBranches { get; set; } 

ApplicantBranchList :

 public class ApplicantBranchList : ViewModel { public ApplicantBranchItem HeaderItem { get; set; } public ApplicantBranchList() { HeaderItem = new ApplicantBranchItem(); } public void MapFromEntityList(IEnumerable<ApplicantBranch> applicantBranches) { var service = new BranchService(DbContext); var selectedIds = applicantBranches.Select(b => b.BranchId); Items = service.ReadBranches() .Where(i => !i.IsDeleted) .Select(p => new ApplicantBranchItem { BranchName = p.Name, WillWorkAt = selectedIds.Contains(p.Id) }); } public IEnumerable<ApplicantBranchItem> Items { get; set; } } 

ApplicantBranchList has its own editor template and an internal editor template for each element in the ApplicantBranchList :

Views / Shared / EditorTemplates / ApplicantBranchList.cshtml:

 @model Comair.RI.UI.Models.ApplicantBranchList <table> <tr> <th style="display: none;"></th> <th> @Html.DisplayNameFor(model => model.HeaderItem.BranchName) </th> <th> @Html.DisplayNameFor(model => model.HeaderItem.WillWorkAt) </th> </tr> @foreach (var item in Model.Items) { @Html.EditorFor(m => item) } </table> 

Views / Shared / EditorTemplates / ApplicantBranchItem.cshtml:

 @model Comair.RI.UI.Models.ApplicantBranchItem <tr> <td style="display: none;"> @Html.HiddenFor(m => m.BranchId) </td> <td> @Html.DisplayFor(m => m.BranchName) </td> <td> @Html.EditorFor(m => m.WillWorkAt) </td> </tr> 

This editor displays correctly in the view, but in the post action:

 public ActionResult Create(ApplicantProfileModel model) { if (ModelState.IsValid) { var branches = model.PreferedBranches; 

PreferedBranches.Items null .

What am I doing wrong?

+8
asp.net-mvc asp.net-mvc-3 asp.net-mvc-4
source share
2 answers

The problem is that ASP.NET cannot figure out how to bind to the Model.Items property.

To fix this, replace:

 public IEnumerable<ApplicantBranchItem> Items { get; set; } 

with this:

 public List<ApplicantBranchItem> Items { get; set; } 

and instead of:

 @foreach (var item in Model.Items) { @Html.EditorFor(m => item) } 

use this one:

 @for (var i = 0; i < Model.Items.Count; i++) { @Html.EditorFor(model => model.Items[i]) // binding works only with items which are accessed by indexer } 
+13
source share

With MVC and editor templates, you don’t need to manually navigate through the list and call @HTMLEditorFor.

Performing this action:

 @Html.EditorFor(model => model.Items) 

matches with:

 @for (var i = 0; i < Model.Items.Count; i++) { @Html.EditorFor(model => model.Items[i]) // binding works only with items which are accessed by indexer } 

MVC iterates through your elements and generates an editor template once for each element. As noted in the comments, your template should be named the same as your model. In addition, your model definition should be the only representation of your model, and not of type IEnumerable. Finally, as noted in the comments, if you specify the template name parameter in your @ Html.EditorFor () call, you will not have the benefit of automatically iterating over your collection. You will need to manually iterate as shown above.

+10
source share

All Articles