2012-07-01 100 views
15

我有一個Razor視圖內下拉列表中的項目列表。在數據庫中,每個項目都有3個與之關聯的值 - 數據庫ID,短名稱(用於顯示)和長名稱(用於傳遞給服務)。下拉菜單必須顯示短名稱,所以我使用數據庫ID作爲值填充下拉列表,短名稱作爲文本。添加屬性以選擇列表選項

但是,當用戶選擇一個項目時,我需要將長名稱作爲查詢參數傳遞給使用jQuery的搜索服務,例如,當選擇Cortina時,需要將「Ford Cortina 1979 Blue」傳遞給該服務。我的第一個想法是將長名稱存儲爲數據短線屬性,但我想知道是否有更好的方法。所以

  • 如何將所有3個值存儲在下拉列表中?
  • 如果我確實使用數據短線屬性,我該如何將所有LONG_NAME值合併到Html.DropDownListFor中,或者以某種方式將它們添加到下拉列表中?

DB:

CARID SHORT_NAME LONG_NAME 
1  Viper  Dodge Viper 1982 
2  Boxster Porsche Boxster 2009 Black 
3  Cortina Ford Cortina 1979 Blue 

控制器助手向下創建降:

public static IEnumerable<SelectListItem> GetSelectList(this IEFRepository repository, string typeName) 
{ 
    var vehicle = repository.TypeTypes.FirstOrDefault(t => t.Name.ToUpper() == typeName); 
    if (vehicle != null) 
    { 
     var carList = vehicle.SubTypes.ToList().OrderBy(s => s.Name); 
     var selectList = new SelectList(subTypeList, "SubTypeID", "Name"); 

     return selectList; 
    } 
} 

下面是我用它來創建下拉代碼:

<div class="editor-field"> 
    @Html.DropDownListFor(model => model.CarID, 
     new SelectList(ViewBag.Cars, "Value", "Text", "1")) 
    @Html.ValidationMessageFor(model => model.CarShortName) 
</div> 

這裏的輸出:

<select id="CarID" name="CarID" data-val="true" data-val-number="The field CarID must be a number." data-val-required="The CarID field is required."> 
    <option value="2">Boxster</option> 
    <option value="3">Cortina</option> 
    <option selected="selected" value="1">Viper</option> 
</select> 
+1

我遇到過類似的情況。我必須有一個允許用戶在對象圖中選擇記錄的下拉菜單。有4個相關表格,每個表格大約有5個字段。我最終不得不編寫一個javascript API來解構DropDownListFor生成的html,然後重新創建一個下拉列表以替換原始列表。我在這裏問了幾個問題,但從來沒有得到任何好的迴應,我覺得'DropDownListFor'在框架中是一種被遺忘的後繼者。 –

回答

1

現在就回到這個。雖然@ nikeaa的答案當然是一個可行的解決方案,但我認爲這是一個重量級,尤其是使用XDocument。提醒我正在處理的是TypeType(Cars)和SubType(汽車類型列表 - Viper,Granada,Hunter,Zodiac,Wolsley 1660等)。 TypeType也可能是卡車,自行車等 因此,這裏是我如何解決它:

我加在控制器上一個JsonResult方法與3個屬性,我想返回一個匿名對象:

public class VehicleController : Controller 
{ 
    // etc. 
    public JsonResult GetSubTypesForTypeType(string typeTypeName) 
    { 
     var cars = pronova2Repository.GetTypeWithSubTypes(typeTypeName); 

     return cars == null 
     ? Json(new object[0], JsonRequestBehavior.AllowGet) 
     : Json(cars.SubTypes.OrderBy(s => s.Name).Select(
      s => new { s.SubTypeID, s.Name, s.Description }).ToArray(), 
      JsonRequestBehavior.AllowGet); 
    } 
    // etc. 
} 

然後,在JS:

填充下拉:

// populate the cars drop down when the select list is available 
if ($('select#SubTypeID').length) { 
    var carsSelect = $('select#SubTypeID'); 
    var carsList = populateCarsList("CARS"); 
    var carsListHtml = createCarsSelectList(carsList); 
    carsSelect.html(''); 
    carsSelect.append(carsListHtml); 

    $('#SubTypeID').change(function (e) { 
     clearFormData(); 
    }); 
} 

調用一個函數通過Ajax調用來獲取亞型(汽車):

function populateCarsList(typeTypeName) { 
    var carsList; 

    $.ajax({ 
     url: '/Vehicle/GetSubTypesForTypeType', 
     data: { typeTypeName: typeTypeName }, 
     async: false 
    }).done(function (data) { 
     carsList = data; 
    }).error(function (msg, url, line) { 
     alert("Error retrieving cars from Vehicle/GetSubTypesForTypeType. Error message: " + line); 
    }); 

    return carsList; 
} 

函數來創建與添加的說明選擇列表中的「數據 - *」屬性:

function createCarsSelectList(selectData) { 
    var html = '', 
     len = selectData.length, 
     selected, 
     description; 

    for (var i = 0; i < len; i++) { 

     // "Viper" should be selected by default 
     if (selectData[i].Name.toLocaleUpperCase() === "VIPER") { 
      selected = ' selected="selected" '; 
     } else { 
      selected = ''; 
     } 

     // Add the description (as a "data-" attribute), some descritions are null 
     if (selectData[i].Description != null) { 
      description = selectData[i].Description; 
     } else { 
      description = ''; 
     } 

     html += '<option value="' + selectData[i].SubTypeID + '" data-description="' + description + '"' + selected + '>' + selectData[i].Name + '</option>'; 
    } 

    return html; 
} 
+3

爲了查找一個值,解析XML遠比服務器往返「重量更輕」。但jQuery更有趣。 – drzaus

+2

這兩個解決方案的區別不是「權重」,而是客戶端與服務器端。 – friggle

7

在應該接收表單提交的控制器操作中,您可以使用選定值的ID來查詢數據庫以獲取長顯示名稱並執行您打算使用它的任何操作。

DropDownListFor幫助程序不支持將HTML5 data-*屬性添加到選項中,但即使它們不會將它們作爲標準表單提交的一部分發送。您將不得不使用JavaScript將它們發送到服務器使用其他技術(隱藏字段,AJAX,查詢字符串參數,...)。

但如果出於某種原因需要選項標籤上的附加屬性,您可以隨時編寫一個自定義幫助程序。

+1

我不會爲服務調用提交表單我會用jQuery做客戶端,所以這就是爲什麼我在初始填充下拉列表時需要檢索長名稱的原因。 –

+0

jQuery腳本會用這個值做什麼? –

+0

將被傳遞給使用jQuery來檢索更多數據的服務,例如$ .ajax({url:「http:// myservice /?suggestWord =」+ carLongName +「&callback =?」 –

19

我有一個類似的情況,我需要傳遞第三個值給每個列表項以確定在jQuery函數中採取的操作。這裏是我的解決方案(這將讓你的任何數量的屬性添加到每個項目在下拉):

首先,我創建了一個SelectListItemWithAttributes類,如下所示:

public class SelectListItemWithAttributes : SelectListItem { 
     public IDictionary<string, string> HtmlAttributes { get; set; } 
    } 

這允許我創建具有附加屬性的選擇列表的項目。

public static MvcHtmlString DropDownListWithItemAttributesFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, IEnumerable<SelectListItemWithAttributes> selectList) { 
    string name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); 

    var selectDoc = XDocument.Parse(htmlHelper.DropDownList(name, (IEnumerable<SelectListItem>)selectList).ToString()); 

    var options = from XElement el in selectDoc.Element("select").Descendants() 
          select el; 

    for (int i = 0; i < options.Count(); i++){ 
     var option = options.ElementAt(i); 
     var attributes = selectList.ElementAt(i); 

     foreach (var attribute in attributes.HtmlAttributes){ 
        option.SetAttributeValue(attribute.Key, attribute.Value); 
     } 
    } 

    selectDoc.Root.ReplaceNodes(options.ToArray()); 
    return MvcHtmlString.Create(selectDoc.ToString()); 
} 

這允許我創建一個下拉使用新SelectListWithAttributes類的屬性:

其次,我如下創建了一個名爲DropDownListWithItemAttributesFor的HTML輔助方法。基本上,它爲下拉列表創建HTML,將其解析爲XML文檔,然後將HtmlAttributes數組中的所有項作爲附加屬性添加到下拉菜單中的每個項目。

三,在我的視圖模型的代碼,我有以下幾點:

private List<SelectListItemWithAttributes> pDropDownDatas = null; 
public List<SelectListItemWithAttributes> DropDownDatas { 
    get { 
     var DropDownDataItems = (
      from c in db.GetDropDownDataList(1, 1000) 
      where c.AccountTypeID == this.AccountTypeID 
      select new SelectListItemWithAttributes() { Text = c.Title, Value = c.ID.ToString(), HtmlAttributes = new Dictionary<string, string> { { "data-callback", c.RequiresCallback.ToString().ToLower() } } }).ToList() 
      ; 

     DropDownDataItems.Insert(0, new SelectListItemWithAttributes() { Text = "-- Select --", Value = "", HtmlAttributes = new Dictionary<string, string> { { "data-callback", "false" } } }); 

     return DropDownDataItems; 
    } 
} 

這種生產能夠將最終填充下拉SelectListItemsWithAttributes名單。這可能是在一個控制器而不是視圖模型,我只是選擇使它成爲我的viewmodel的一個屬性。

最後,在視圖中它是這樣的:

@Html.DropDownListWithItemAttributesFor(m => m.DropDownDataID, Model.DropDownDatas) 

這將顯示下拉使用從包含SelectListItemsWithAttributes列表視圖模型屬性頁面上。

我從互聯網上找到的各種解決方案構建了這個解決方案,所以它不是我原創的,但是我把它放在一起工作。

希望這會幫助你解決你的問題。

+0

我假設你解析原始Helper的輸出以避免創建自己的TagBuilder並重寫所有正常發生的魔法? – drzaus

+0

@drzaus - 是這行代碼:var selectDoc = XDocument.Parse(htmlHelper.DropDo wnList(name,(IEnumerable )selectList).ToString()); – nikeaa

+1

這真是太好了。但是,使用GetFullHtmlFieldName最終會創建帶有雙前綴的DropDownList,即「ModelName_ModelName_MemberName」。我不得不將字符串名稱行更改爲:string name = ExpressionHelper.GetExpressionText(expression); – friggle

21

每個人都忘記了「經典」的方式來解決這些問題:使用foreach循環,實際上編寫輸入的html。唯一的缺點是你必須添加自動屬性的東西(如驗證等),這取決於你的目的可能不是什麼大不了的。

喜歡的東西:

<select> // add other attributes as expected 
@foreach(var type in Model.MyFancyTypes) { 
<option value="@type.SubTypeID" data-description="@type.Description" 
    @if(ViewBag.TypeSelected == type.SubTypeID) { 
     selected="selected" 
    }>@type.Name</option> 
} 
</select> 
+2

如果檢查視圖中的選擇「有異味」,則始終可以構建適當的ViewModel和相應的部分。 – drzaus

+0

偉大的解決方案。我們有時會忘記我們的基礎。謝謝你提醒我們。 – pqsk

+0

我試過這個,但它要求「;」選中的行後面的分號=「選中」。 Intellisense顯示它仍然在c#上下文中,而不是html上下文。請幫忙! – singsuyash

1

@nikeaa謝謝你的代碼。我發現它有一些問題(例如,當選項列表爲空時,選擇不正確;您不需要替換選項,只需修改它們,否則選擇的某些屬性將被刪除),並且我添加了一些額外的參數來充分利用DropDownListFor的威力。這裏是我的版本的方法:

public static MvcHtmlString DropDownListWithItemAttributesFor<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItemWithAttributes> selectList, 
    string optionLabel, IDictionary<string, object> htmlAttributes) 
{ 
    if (selectList == null || !selectList.Any()) 
     return htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes); 

    var selectDoc = XDocument.Parse(htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes).ToString()); 

    var options = selectDoc.Element("select").Descendants().ToArray(); 

    for (int i = 0; i < options.Length; i++) 
    { 
     var option = options[i]; 
     var attributes = selectList.ElementAt(i); 

     foreach (var attribute in attributes.Attributes) 
      option.SetAttributeValue(attribute.Key, attribute.Value); 
    } 

    return MvcHtmlString.Create(selectDoc.ToString()); 
} 
相關問題