MVC DropDownList with EditorTemplate showing Selected Value and invalid name field as propertyName.propertyName

StackOverflow https://stackoverflow.com/questions/19186607

Question

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>
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top