Html.dropdownlistにHTMLクラスタグを追加する方法に関する回答を探しています。ここにコードがあります
<%: Html.DropDownList("PackageId", new SelectList(ViewData["Packages"] as IEnumerable, "PackageId", "Name", Model.PackageId))%>
Select要素の下にオプションのクラスを追加して、この連鎖selectを使用できるようにします。
<select id="category">
<option value="1">One</option>
<option value="2">Two</option>
</select>
<select id="package">
<option value="1" class="1">One - package1</option>
<option value="2" class="1">One - package2</option>
<option value="3" class="2">Two - package1</option>
<option value="4" class="2">Two - package2</option>
</select>
$("#series").chained("#mark");
これは、使用するDropDownListではなく、DropDownlistFor拡張メソッドに対して行いましたが、おそらく自分で理解できるでしょう。このようなものは主にMVCソースからのコピー/貼り付けです。ソースを見つけることができます こちら 。
public class ExtendedSelectListItem : SelectListItem
{
public object htmlAttributes { get; set; }
}
public static partial class HtmlHelperExtensions
{
public static MvcHtmlString ExtendedDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, object htmlAttributes)
{
return SelectInternal(htmlHelper, optionLabel, ExpressionHelper.GetExpressionText(expression), selectList, false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, string optionLabel, string name, IEnumerable<ExtendedSelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(fullName))
throw new ArgumentException("No name");
if (selectList == null)
throw new ArgumentException("No selectlist");
object defaultValue = (allowMultiple) ? GetModelStateValue(htmlHelper, fullName, typeof(string[])) : GetModelStateValue(htmlHelper, 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)
defaultValue = htmlHelper.ViewData.Eval(fullName);
if (defaultValue != null)
{
IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture);
HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
List<ExtendedSelectListItem> newSelectList = new List<ExtendedSelectListItem>();
foreach (ExtendedSelectListItem item in selectList)
{
item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
newSelectList.Add(item);
}
selectList = newSelectList;
}
// Convert each ListItem to an <option> tag
StringBuilder listItemBuilder = new StringBuilder();
// Make optionLabel the first item that gets rendered.
if (optionLabel != null)
listItemBuilder.Append(ListItemToOption(new ExtendedSelectListItem() { Text = optionLabel, Value = String.Empty, Selected = false }));
foreach (ExtendedSelectListItem item in selectList)
{
listItemBuilder.Append(ListItemToOption(item));
}
TagBuilder tagBuilder = new TagBuilder("select")
{
InnerHtml = listItemBuilder.ToString()
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */);
tagBuilder.GenerateId(fullName);
if (allowMultiple)
tagBuilder.MergeAttribute("multiple", "multiple");
// If there are any errors for a named field, we add the css attribute.
ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name));
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
internal static string ListItemToOption(ExtendedSelectListItem item)
{
TagBuilder builder = new TagBuilder("option")
{
InnerHtml = HttpUtility.HtmlEncode(item.Text)
};
if (item.Value != null)
{
builder.Attributes["value"] = item.Value;
}
if (item.Selected)
{
builder.Attributes["selected"] = "selected";
}
builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(item.htmlAttributes));
return builder.ToString(TagRenderMode.Normal);
}
}
@ john-landheerのソリューションの少し改善されたバージョンを次に示します。
改善されたもの:
GetModelStateValue()
の問題を修正DropDownList()
拡張メソッドが追加されました控えめな検証属性は、必要なようにレンダリングされます
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace App.Infrastructure.Helpers
{
public class ExtendedSelectListItem : SelectListItem
{
public object HtmlAttributes { get; set; }
}
public static class ExtendedSelectExtensions
{
internal static object GetModelStateValue(this HtmlHelper htmlHelper, string key, Type destinationType)
{
System.Web.Mvc.ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(key, out modelState))
{
if (modelState.Value != null)
{
return modelState.Value.ConvertTo(destinationType, null /* culture */);
}
}
return null;
}
public static MvcHtmlString ExtendedDropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<ExtendedSelectListItem> selectList)
{
return ExtendedDropDownList(htmlHelper, name, selectList, (string)null, (IDictionary<string, object>)null);
}
public static MvcHtmlString ExtendedDropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
{
return ExtendedDropDownListHelper(htmlHelper, null, name, selectList, optionLabel, htmlAttributes);
}
public static MvcHtmlString ExtendedDropDownListHelper(this HtmlHelper htmlHelper, ModelMetadata metadata, string expression, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
{
return SelectInternal(htmlHelper, metadata, optionLabel, expression, selectList, false, htmlAttributes);
}
public static MvcHtmlString ExtendedDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, IEnumerable<ExtendedSelectListItem> selectList,
string optionLabel, object htmlAttributes)
{
if (expression == null)
throw new ArgumentNullException("expression");
ModelMetadata metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
return SelectInternal(htmlHelper, metadata, optionLabel, ExpressionHelper.GetExpressionText(expression), selectList,
false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name,
IEnumerable<ExtendedSelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(fullName))
throw new ArgumentException("No name");
if (selectList == null)
throw new ArgumentException("No selectlist");
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)
defaultValue = htmlHelper.ViewData.Eval(fullName);
if (defaultValue != null)
{
IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
IEnumerable<string> values = from object value in defaultValues
select Convert.ToString(value, CultureInfo.CurrentCulture);
HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
List<ExtendedSelectListItem> newSelectList = new List<ExtendedSelectListItem>();
foreach (ExtendedSelectListItem item in selectList)
{
item.Selected = (item.Value != null)
? selectedValues.Contains(item.Value)
: selectedValues.Contains(item.Text);
newSelectList.Add(item);
}
selectList = newSelectList;
}
// Convert each ListItem to an <option> tag
StringBuilder listItemBuilder = new StringBuilder();
// Make optionLabel the first item that gets rendered.
if (optionLabel != null)
listItemBuilder.Append(
ListItemToOption(new ExtendedSelectListItem()
{
Text = optionLabel,
Value = String.Empty,
Selected = false
}));
foreach (ExtendedSelectListItem item in selectList)
{
listItemBuilder.Append(ListItemToOption(item));
}
TagBuilder tagBuilder = new TagBuilder("select")
{
InnerHtml = listItemBuilder.ToString()
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */);
tagBuilder.GenerateId(fullName);
if (allowMultiple)
tagBuilder.MergeAttribute("multiple", "multiple");
// If there are any errors for a named field, we add the css attribute.
System.Web.Mvc.ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fullName, metadata));
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
internal static string ListItemToOption(ExtendedSelectListItem item)
{
TagBuilder builder = new TagBuilder("option")
{
InnerHtml = HttpUtility.HtmlEncode(item.Text)
};
if (item.Value != null)
{
builder.Attributes["value"] = item.Value;
}
if (item.Selected)
{
builder.Attributes["selected"] = "selected";
}
builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(item.HtmlAttributes));
return builder.ToString(TagRenderMode.Normal);
}
}
}
これは、ASP.NET MVCに組み込まれているDropDownListヘルパーでは不可能です。そのため、必要な場合は独自のヘルパーを作成する必要があります。 TagBuilderを使用してオプションを生成するASP.NET MVCのソースコードを見て、カスタム実装に属性を追加できます。別のよりエレガントなソリューションは、ビュー内のデータセットを手動でループし、個々のオプション要素を生成することです。
ここで最初に思い浮かぶのは、JQueryです。次のコードで簡単にこれを行うことができます:
$("#bla").find("option").addClass("poo");
Htmlを単に変更するラッパーを作成しました。
public static MvcHtmlString DisableFirstItem(MvcHtmlString htmlString)
{
return new MvcHtmlString(
htmlString.ToString()
.Replace("<option value=\"Unknown\">",
"<option disabled value=\"Unknown\">")
);
}
そして、このヘルパー関数でDropDownListForをラップしました:
@Html.Raw(MyHtmlHelpers.DisableFirstItem(
Html.DropDownListFor(m => m.Instrument,
new SelectList(ReflectionHelpers.GenerateEnumDictionary<OrderInstrument>(true), "Key", "Value", Model.Instrument),
new { @class = "form-control" })
))
必要に応じて、ヘルパー機能をより洗練させることができます。