Razorビュー内のドロップダウンリストに項目のリストがあります。データベースでは、各アイテムに3つの値が関連付けられています。データベースID、ショートネーム(表示用)、ロングネーム(サービスへの受け渡し用)です。ドロップダウンには短い名前が表示されている必要があるため、値としてデータベースIDを、テキストとして短い名前をドロップダウンに入力しています。
ただし、ユーザーがアイテムを選択した場合、jQueryを使用してクエリパラメータとして長い名前を検索サービスに渡す必要があります。たとえば、Cortinaが選択された場合、「Ford Cortina1979Blue」をサービスに渡す必要があります。私の最初の考えは、長い名前をデータダッシュ属性として保存することですが、もっと良い方法があるのではないかと思います。そう
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>
今これに戻ってください。 @nikeaaの答えは確かに実行可能な解決策ですが、特にXDocumentを使用すると、少し重いと思いました。私が扱っているのは、TypeType(Cars)とSubType(Viper、Granada、Hunter、Zodiac、Wolsley 1660などの車のタイプのリスト)です。 TypeTypeは、Trucks、Bicyclesなどの場合もあります。これが私がそれを解決した方法です。
コントローラーに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;
}
「data- *」属性として説明が追加された選択リストを作成する関数:
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;
}
誰もがこれらの問題を解決するための「古典的な」方法を忘れています。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>
同様の状況で、jQuery関数で実行するアクションを決定するために各リスト項目に3番目の値を渡す必要がありました。これが私の解決策です(ドロップダウンの各アイテムに任意の数の属性を追加できます):
まず、次のようにSelectListItemWithAttributesクラスを作成しました。
public class SelectListItemWithAttributes : SelectListItem {
public IDictionary<string, string> HtmlAttributes { get; set; }
}
これにより、追加の属性を付加して選択リストのアイテムを作成できます。
次に、DropDownListWithItemAttributesForというHTMLヘルパーメソッドを次のように作成しました。
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クラスを属性として使用してドロップダウンを作成できます。基本的には、ドロップダウンリストのHTMLを作成し、それをXMLドキュメントに解析してから、HtmlAttributes配列のアイテムを追加属性としてドロップダウンの各アイテムに追加します。
第三に、私のViewModelコードには、次のものがあります。
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のリストが作成されます。これはビューモデルの代わりにコントローラにある可能性があります。私はそれをビューモデルのプロパティにすることを選びました。
最後に、ビューでは次のようになります。
@Html.DropDownListWithItemAttributesFor(m => m.DropDownDataID, Model.DropDownDatas)
これにより、SelectListItemsWithAttributesのリストを含むviewmodelのプロパティを使用して、ページにドロップダウンが表示されます。
このソリューションはインターネットで見つけたさまざまなソリューションから作成したので、すべてが独創的ではありませんでしたが、うまく機能するものにまとめました。
これが問題の解決に役立つことを願っています。
フォーム送信を受信することになっているコントローラーアクション内で、選択した値のIDを使用してデータベースにクエリを実行し、長い表示名を取得して、意図したとおりに実行できます。
DropDownListFor
ヘルパーはHTML5の追加をサポートしていませんdata-*
オプションの属性ですが、そうした場合でも、標準のフォーム送信の一部として送信されません。別の手法(非表示フィールド、AJAX、クエリ文字列パラメーターなど)を使用してサーバーに送信するには、JavaScriptを使用する必要があります。
ただし、何らかの理由でオプションタグに追加の属性が必要な場合は、いつでもカスタムヘルパーを作成できます。
@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());
}