I have a model, ApplicantBranchList
, 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 inner editor template for each item in 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 renders properly in the view, but in the post action:
public ActionResult Create(ApplicantProfileModel model)
{
if (ModelState.IsValid)
{
var branches = model.PreferedBranches;
PreferedBranches.Items
is null
.
What am I doing wrong?
With MVC and editor templates you don't need to manually move through a list and call @HTMLEditorFor.
Doing this:
@Html.EditorFor(model => model.Items)
is the same as:
@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 will handle the iteration through your items and generate your editor template once per item. As is noted in the comments your template must be named the same as your model. Also, your model definition should be a singular representation of your model, not of type IEnumerable. Lastly, as noted in the comments, if you specify the template name parameter in your call to @Html.EditorFor() you will not have the benefit of the automatic iteration over your collection. You will need to manually iterate as is demonstrated above.
The problem is that ASP.NET can't figure out how to bind to Model.Items
property.
To to fix it 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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With