2016-03-03 56 views
0

我正在使用MVC3從數據庫填充HTML表單。許多表單元素是下拉列表(選擇)。我使用「DropDownListFor」助手來預先填充列表並在編輯後的數據的POSTing上執行數據綁定(工作正常)。當預先填充空值時,DropDownListFor幫助器會進行錯誤選擇

我在數據庫中有幾個可以爲空的字段,其中「NULL/Empty String」是一個合法的默認值。因此,使用「高」,「中」和「」三個可能的選項而不是「高」,「中」和「低」,其中空字符串或空值等於「低」。我在下拉列表中鏡像了這種行爲,以便它們提供相應的文本/值對。

問題出在模型綁定時,它預先填充下拉列表並選擇基於數據庫中的值顯示在DDL中的默認值。如果傳入的數據庫值爲「高」,則正確選擇「高」選項。如果數據庫值是「中等」,則適當選擇「中等」選項。但 - 如果數據庫的值是NULL /空字符串,它會選擇「medium」選項而不是「low」選項(或者根本沒有「selected」選項,這正是我所期望的)!它會根據我使用的DropDownListFor重載在列表頂部正確插入默認的optionLabel,但是通過在「medium」選項上標記'selected ='選擇'',它會及時覆蓋它 - 來自哪裏?

這裏是控制器中的代碼;

IcxPdcEditViewModel editViewModel = new IcxPdcEditViewModel() 
{ 
    TwoPlusEmptyDDL = twoPlusEmptyList.TwoPlusEmptyList 
}; 
return View(editViewModel); 

其中幫助者如下設置DDL;

public class PDCTwoPlusEmptyList 
{ 
    public List<SelectListItem> TwoPlusEmptyList { get; set; } 
    public PDCTwoPlusEmptyList() 
    { 
     this.TwoPlusEmptyList = new List<SelectListItem>() 
     { 
      new SelectListItem() { Text = "medium", Value = "medium" }, 
      new SelectListItem() { Text = "high", Value = "high" } 
     }; 
    } 
} 

這裏是視圖中的相應代碼;

<td>Maintenance:</td> 
<td><%= Html.DropDownListFor(model => model.Plant.Maintenance, Model.TwoPlusEmptyDDL, "low") %></td> 

這是數據庫值爲NULL /空字符串(已確認)時生成的HTML源代碼;

<td><b>Maintenance</b>*:</td> 
<td><select id="Plant_Maintenance" name="Plant.Maintenance"> 
    <option value="">low</option> 
    <option selected="selected" value="medium">medium</option> 
    <option value="high">high</option> 
</select></td> 

最後一個想法,我根本沒有使用ViewBag,並且在ViewData中沒有衝突的名稱。

有什麼想法是什麼導致這種行爲?

+0

可以選擇''medium「'唯一的方法就是如果'Maint enance'是'「medium」'。但有些東西看起來不正確,因爲它的生成id =「_ Maintenance」(不應該有下劃線字符),所以其他事情正在發生。 –

+0

我編輯了代碼以顯示完整的ID和名稱 - 爲了清晰起見,我刪除了它們,但我想這可能會導致一些混淆。我可以從一些非常活躍的調試中向你保證,當選擇「medium」時,數據庫字段「Maintenance」是NULL。我無法解釋它。 – Sylver

+0

我可以保證'Plant.Maintenance'的值爲'null',那麼將選擇第一個(「低」)選項(您可以查看[源代碼](https://aspnetwebstack.codeplex .com/SourceControl/latest#src/System.Web.Mvc/Html/SelectExtensions.cs),它顯示helper在內部生成一個新的IEnumerable ,並根據模型的值設置Selected屬性)。我建議你在你的視圖中包含一個'

@Model.Plant.Maintenance
'並檢查輸出 –

回答

0

我發現並解決了這個問題,這要歸功於大量的調試! :)

問題是,我正在單個窗體(即單個視圖)上重複使用單個「PDCTwoPlusEmptyList」實例作爲4種不同的下拉列表,以此來節省控制器中的代碼。這個「可重用的」DDL對象能夠正確地重置「Selected」標誌,以匹配值爲NULL時從數據庫傳入的值,在這種情況下,它會「記住」來自先前DDL的值。解決方案是爲頁面上的每個DDL創建單獨的PDCTwoPlusEmptyList實例 - 現在一切正常。學過的知識!

0

我遇到了同樣的問題 - 重複使用List<SelectListItem>,並發現當值爲空時,所選值未被DropDownListFor正確設置。在步入System.Web.Mvc的代碼後,我找到了原因。

調用堆棧看起來是這樣的:

public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes) 
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) 
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes) 

而對於SelectInternal的代碼如下所示:

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes) 
{ 
    string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); 

    // ... 

    bool usedViewData = false; 

    // If we got a null selectList, try to use ViewData to get the list of items. 
    if (selectList == null) 
    { 
     selectList = htmlHelper.GetSelectData(name); 
     usedViewData = true; 
    } 

    object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string)); 

    // If we haven't already used ViewData to get the entire list of items then we need to 
    // use the ViewData-supplied value before using the parameter-supplied value. 
    if (defaultValue == null && !String.IsNullOrEmpty(name)) 
    { 
     if (!usedViewData) 
     { 
      defaultValue = htmlHelper.ViewData.Eval(name); 
     } 
     else if (metadata != null) 
     { 
      defaultValue = metadata.Model; 
     } 
    } 

    if (defaultValue != null) 
    { 
     selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple); 
    } 

    // ... 
} 

SelectInternal被調用時,defaultValue變量是用來存放從價值模型。這是測試null,如果值爲而不是null,那麼selectList將被覆蓋一個新的列表,並且爲與defaultValue匹配的列表項設置所選屬性。

如果到SelectInternal的後續調用將默認值null,那麼selectList沒有更新,所以在列表中選擇的項目保持相同的最近調用了一個非空值和所使用的相同selectList

處理這一問題的一些可能的選項:

  • 使用每個列表的單獨列表(由this answer建議)
  • 編寫清除所選屬性對所有列表項自定義的HtmlHelper
  • 在將其傳遞到DropDownListFor之前克隆列表,因此原始列表永遠不會被修改