I have a dropdown list that is rendered through an EditorTemplate. The property has UIHints in a Validation class and displays correctly but when I look at the HTML the name of the control is PropertyType.PropertyName rather than just PropertyName.
This prevents the model from binding.
I also can't return a selected value to the View.
How do I get around this?
UPDATE See answer below for details.
ViewModel
public partial class HouseholdEditViewModel
{
public int householdID { get; set; }
public int familyID { get; set; }
public string address { get; set; }
public HousingTypeDropDownViewModel housingType { get; set; }
public KeyworkerDropDownViewModel keyworker { get; set; }
public string attachmentDate { get; set; }
public bool loneParent { get; set; }
public string familyPhoneCode { get; set; }
public string familyPhone { get; set; }
}
DropDown ViewModel
public class HousingTypeDropDownViewModel
{
public int housingTypeID { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
EditorTemplate
@model WhatWorks.ViewModels.HousingTypeDropDownViewModel
@Html.DropDownListFor(h => h.housingTypeID, new SelectList(Model.Items, "Value", "Text"))
View
using (Html.ControlGroupFor(property.Name))
{
@Html.Label(property.GetLabel(), new { @class = "control-label" })
<div class="controls">
@Html.Editor(property.Name, new { @class = "input-xlarge" })
@Html.ValidationMessage(property.Name, null, new { @class = "help-inline" })
</div>
}
HTML
<div class="control-group">
<label class="control-label" for="Housing_Type">Housing Type</label>
<div class="controls">
<select data-val="true" data-val-number="The field housingTypeID must be a number." data-val-required="The housingTypeID field is required." id="housingType_housingTypeID" name="housingType.housingTypeID">
<option value="1">Owner Occupied</option>
<option value="2">Rented - Social Landlord</option>
</select>
<span class="field-validation-valid help-inline" data-valmsg-for="housingType" data-valmsg-replace="true"></span>
</div>
</div>
Having researched this a bit more, it would seem that this is working as intended.
The simplest workaround is to use a DropDownList in the EditorTemplate rather than the DropDownListFor. Set the name
to a blank string and the Html.Editor only picks up the property name once. Also note the change to Model.Items
for the SelectList
@model WhatWorks.ViewModels.HousingTypeDropDownViewModel
@Html.DropDownList("", Model.Items)
To elaborate a bit further on this, I was trying to use a DropDownList
with an IEnumerable<SelectListItem>
from my ViewModel and was struggling to get my HTML tags rendering correctly. Part of this involved needing a Selected Value
on my Edit View.
As this seems to be a regular problem, I've amended the title of this post slightly and will document Controller code here - this is what worked for me and may not be best practice... Caveat Emptor!
IRepository
public interface IHouseholdRepository : IDisposable
{
IEnumerable<tHousehold> Get();
tHousehold GetById(int Id);
IEnumerable<SelectListItem> AddHousingType();
IEnumerable<SelectListItem> EditHousingType(int id);
//etc...
}
Repository
public IEnumerable<SelectListItem> AddHousingType()
{
var query = from ht in context.tHousingType
orderby ht.housingType
select new
{
ht.housingTypeID,
ht.housingType
};
return query.AsEnumerable()
.Select(s => new SelectListItem
{
Value = s.housingTypeID.ToString(),
Text = s.housingType
});
}
public IEnumerable<SelectListItem> EditHousingType(int id)
{
var housingType = (from h in context.tHousehold
where h.householdID == id
select new
{
h.tHousingStatus.FirstOrDefault().housingTypeID
}
);
var query = from ht in context.tHousingType
orderby ht.housingType
select new
{
ht.housingTypeID,
ht.housingType
};
return query.AsEnumerable()
.Select(s => new SelectListItem
{
Value = s.housingTypeID.ToString(),
Text = s.housingType,
Selected = (housingType.FirstOrDefault().housingTypeID == s.housingTypeID ? true : false)
});
}
Controller
public ActionResult Edit(int id = 0)
{
HousingTypeDropDownViewModel housingType = new HousingTypeDropDownViewModel
{
Items = _repo.EditHousingType(id)
};
HouseholdEditViewModel h = GetUpdate(id);
//GetUpdate is Automapper code unrelated to the DropDown
h.housingTypeID = housingType;
if (h == null)
{
return HttpNotFound();
}
return View(h);
}
Inspiration for the above taken from too many SO posts to mention. Thanks all and hope this helps others.