最近、このトピックについて多くの議論がありました。日付、日付範囲、および複数選択チェックボックス リストでも、同様の障害が発生します。html コントロールの豊富なセットを使用したい場所ならどこでも。私は子ViewModelの概念を試してきましたが、解決策は私が試した他のアプローチよりもきれいだと思います.
基本的な概念は、カスタム EditorTemplate に密接に結合された小さなビュー モデルを定義することです。
あなたの例では、単一の選択リストに固有の (子) ViewModel から始めます。
public class SelectModel
{
#region SelectModel(string value, IEnumerable<SelectListItem> items)
public SelectModel(string value, IEnumerable<SelectListItem> items)
{
_value = value;
Items = new List<SelectListItem>(items);
_Select();
}
#endregion
// Properties
public List<SelectListItem> Items { get; private set; }
public string Value
{
get { return _value; }
set { _value = value; _Select();}
}
private string _value;
// Methods
private void _Select()
{
Items.ForEach(x => x.Selected = (Value != null && x.Value == Value));
}
}
ドロップダウンを使用したいビュー モデルで、選択モデルを作成します (私たちは皆、ビュー モデルを使用していますよね?)。
public class EmailModel
{
// Constructors
public EmailModel()
{
Priority = new SelectModel("normal", _ToPrioritySelectItems());
}
// Properties
public SelectModel Priority { get; set; }
// Methods
private IEnumerable<SelectListItem> _ToPrioritySelectItems()
{
List<SelectListItem> result = new List<SelectListItem>();
result.Add(new SelectListItem() { Text = "High", Value = "high" });
...
}
これは、ドロップダウン項目の固定セットを使用した単純な例であることに注意してください。それらがドメイン レイヤーからのものである場合、コントローラーはそれらを ViewModel に渡します。
次に、エディター テンプレート SelectModel.ascx を Shared/EditorTemplates に追加します。
<%@ Control Inherits="System.Web.Mvc.ViewUserControl<SelectModel>" %>
<div class="set">
<%= Html.LabelFor(model => model) %>
<select id="<%= ViewData.ModelMetadata.PropertyName %>_Value" name="<%=ViewData.ModelMetadata.PropertyName %>.Value">
<% foreach (var item in Model.Items) { %>
<%= Html.OptionFor(item) %>
<% } %>
</select>
</div>
注: OptionFor は、明白な機能を実行するカスタム拡張機能です。
ここでの秘訣は、デフォルトの ModelBinder が期待する複合形式を使用して ID と名前を設定することです。この例では、「Priority.Value」です。そのため、SelectModel の一部として定義されている文字列ベースの Value プロパティが直接設定されます。セッターは、フォームを再表示する必要がある場合に、アイテムのリストを更新してデフォルトの選択オプションを設定します。
この「子ビュー モデル」アプローチが本当に優れているのは、より複雑な「マークアップのスニペットの制御」です。MultiSelect リスト、開始/終了の日付範囲、および日付と時刻の組み合わせに対して同様のアプローチに従う子ビュー モデルができました。
この道をたどるとすぐに、次の明らかな問題は検証になります。
最終的に、すべての子 ViewModel に標準インターフェイスを実装させました。
public interface IValidatable
{
bool HasValue { get; }
bool IsValid { get; }
}
次に、カスタム ValidationAttribute があります。
public class IsValidAttribute : ValidationAttribute
{
// Constructors
public IsValidAttribute()
{
ErrorMessage = "(not valid)";
}
// Properties
public bool IsRequired { get; set; }
// Methods
private bool Is(object value)
{
return value != null && !"".Equals(value);
}
public override bool IsValid(object value)
{
if (!Is(value) && !IsRequired)
return true;
if (!(value is IValidatable))
throw new InvalidOperationException("IsValidAttribute requires underlying property to implement IValidatable");
IValidatable validatable = value as IValidatable;
return validatable.IsValid;
}
}
これで、スカラー プロパティと同様に子 ViewModel ベースのプロパティに属性を配置できます。
[IsValid(ErrorMessage = "Please enter a valid start date/time")]
public DateAndTimeModel Start { get; set; }