MVC2アプリケーションで作業しており、テキスト入力のmaxlength属性を設定したい。
データアノテーションを使用してModelオブジェクトのstringlength属性をすでに定義しており、入力した文字列の長さを正しく検証しています。
モデルに既に情報がある場合、最大長属性を手動で設定することで、ビューで同じ設定を繰り返したくありません。これを行う方法はありますか?
以下のコードスニペット:
モデルから:
[Required, StringLength(50)]
public string Address1 { get; set; }
ビューから:
<%= Html.LabelFor(model => model.Address1) %>
<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long" })%>
<%= Html.ValidationMessageFor(model => model.Address1) %>
避けたいのは:
<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long", maxlength="50" })%>
この出力を取得したい:
<input type="text" name="Address1" maxlength="50" class="text long"/>
これを行う方法はありますか?
私は熟考することなくこれを達成する方法を知りません。ヘルパーメソッドを書くことができます:
public static MvcHtmlString CustomTextBoxFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
object htmlAttributes
)
{
var member = expression.Body as MemberExpression;
var stringLength = member.Member
.GetCustomAttributes(typeof(StringLengthAttribute), false)
.FirstOrDefault() as StringLengthAttribute;
var attributes = (IDictionary<string, object>)new RouteValueDictionary(htmlAttributes);
if (stringLength != null)
{
attributes.Add("maxlength", stringLength.MaximumLength);
}
return htmlHelper.TextBoxFor(expression, attributes);
}
次のように使用できます:
<%= Html.CustomTextBoxFor(model => model.Address1, new { @class = "text long" })%>
控えめな検証を使用している場合、このクライアント側も処理できます。
$(document).ready(function ()
{
$("input[data-val-length-max]").each(function ()
{
var $this = $(this);
var data = $this.data();
$this.attr("maxlength", data.valLengthMax);
});
});
CustomModelMetaDataProviderを使用してこれを実現します
手順1.新しいCustomModelMetadataProviderクラスを追加する
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
ModelMetadata metadata = base.CreateMetadata(attributes,
containerType,
modelAccessor,
modelType,
propertyName);
//Add MaximumLength to metadata.AdditionalValues collection
var stringLengthAttribute = attributes.OfType<StringLengthAttribute>().FirstOrDefault();
if (stringLengthAttribute != null)
metadata.AdditionalValues.Add("MaxLength", stringLengthAttribute.MaximumLength);
return metadata;
}
}
ステップ2. Global.asaxでCustomModelMetadataProviderを登録します
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ModelMetadataProviders.Current = new CustomModelMetadataProvider();
}
ステップ3. Views/Shared/EditorTemplatesで、String.ascxという名前の部分ビューを追加します
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%if (!ViewData.ModelMetadata.AdditionalValues.ContainsKey("MaxLength")) { %>
<%: Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line" }) %>
<% } else {
int maxLength = (int)ViewData.ModelMetadata.AdditionalValues["MaxLength"];
%>
<%: Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line", MaxLength = maxLength })%>
<% } %>
完了...
編集。ステップ3は、テキストボックスにさらに多くのものを追加する場合、いようになることがあります。この場合、次のことができます。
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
IDictionary<string, object> Attributes = new Dictionary<string, object>();
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("MaxLength")) {
Attributes.Add("MaxLength", (int)ViewData.ModelMetadata.AdditionalValues["MaxLength"]);
}
if (ViewData.ContainsKey("style")) {
Attributes.Add("style", (string)ViewData["style"]);
}
if (ViewData.ContainsKey("title")) {
Attributes.Add("title", (string)ViewData["title"]);
}
%>
<%: Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, Attributes)%>
これをメタデータクラスで機能させるには、次のコードを使用する必要があります。私はそれがきれいではないことを知っていますが、仕事が完了し、EntityクラスとViewの両方でmaxlengthプロパティを書く必要がなくなります:
public static MvcHtmlString TextBoxFor2<TModel, TProperty>
(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
object htmlAttributes = null
)
{
var member = expression.Body as MemberExpression;
MetadataTypeAttribute metadataTypeAttr = member.Member.ReflectedType
.GetCustomAttributes(typeof(MetadataTypeAttribute), false)
.FirstOrDefault() as MetadataTypeAttribute;
IDictionary<string, object> htmlAttr = null;
if(metadataTypeAttr != null)
{
var stringLength = metadataTypeAttr.MetadataClassType
.GetProperty(member.Member.Name)
.GetCustomAttributes(typeof(StringLengthAttribute), false)
.FirstOrDefault() as StringLengthAttribute;
if (stringLength != null)
{
htmlAttr = new RouteValueDictionary(htmlAttributes);
htmlAttr.Add("maxlength", stringLength.MaximumLength);
}
}
return htmlHelper.TextBoxFor(expression, htmlAttr);
}
クラスの例:
[MetadataType(typeof(Person.Metadata))]
public partial class Person
{
public sealed class Metadata
{
[DisplayName("First Name")]
[StringLength(30, ErrorMessage = "Field [First Name] cannot exceed 30 characters")]
[Required(ErrorMessage = "Field [First Name] is required")]
public object FirstName { get; set; }
/* ... */
}
}
私は個人的にjrummelのjquery修正を愛していますが、ここではモデルに単一の真実のソースを維持する別のアプローチを示します...
きれいではありませんが、うまくいきました。私のために...
プロパティの装飾を使用する代わりに、モデルライブラリ/ dllで名前の付いたパブリック定数をいくつか定義し、HtmlAttributesを介してビューでそれらを参照します。
Public Class MyModel
Public Const MAX_ZIPCODE_LENGTH As Integer = 5
Public Property Address1 As String
Public Property Address2 As String
<MaxLength(MAX_ZIPCODE_LENGTH)>
Public Property ZipCode As String
Public Property FavoriteColor As System.Drawing.Color
End Class
次に、カミソリビューファイルのEditorFor ...で、オーバーロードでHtmlAttirubteオブジェクトを使用し、目的のmax-lengthプロパティを指定して定数を参照します。完全修飾された名前空間パスを介して定数を指定する必要があります。 .. MyCompany.MyModel.MAX_ZIPCODE_LENGTH ..モデルからすぐにぶら下がることはありませんが、機能します。
Darinのリフレクションベースのアプローチが特に役立つことがわかりました。メタデータContainerType
をプロパティ情報を取得するための基礎として使用する方が、このメソッドがmvcエディター/ディスプレイテンプレート(TModel
string
)などの単純な型である。
public static MvcHtmlString CustomTextBoxFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
object htmlAttributes
)
{
var metadata = ModelMetadata.FromLambdaExpression( expression, new ViewDataDictionary<TModel>( htmlHelper.ViewDataContainer.ViewData ) );
var stringLength = metadata.ContainerType.GetProperty(metadata.PropertyName)
.GetCustomAttributes(typeof(StringLengthAttribute), false)
.FirstOrDefault() as StringLengthAttribute;
var attributes = (IDictionary<string, object>)new RouteValueDictionary(htmlAttributes);
if (stringLength != null)
{
attributes.Add("maxlength", stringLength.MaximumLength);
}
return htmlHelper.TextBoxFor(expression, attributes);
}
StringLengthまたはその他の属性を取得するために使用できる静的メソッドを次に示します。
using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
public static class AttributeHelpers {
public static Int32 GetStringLength<T>(Expression<Func<T,string>> propertyExpression) {
return GetPropertyAttributeValue<T,string,StringLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}
//Optional Extension method
public static Int32 GetStringLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
return GetStringLength<T>(propertyExpression);
}
//Required generic method to get any property attribute from any class
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
var expression = (MemberExpression)propertyExpression.Body;
var propertyInfo = (PropertyInfo)expression.Member;
var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;
if (attr==null) {
throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
}
return valueSelector(attr);
}
}
静的メソッドの使用...
var length = AttributeHelpers.GetStringLength<User>(x => x.Address1);
または、インスタンスでオプションの拡張メソッドを使用して...
var player = new User();
var length = player.GetStringLength(x => x.Address1);
または、他の属性に完全な静的メソッドを使用して...
var length = AttributeHelpers.GetPropertyAttributeValue<User,string,StringLengthAttribute,Int32>(prop => prop.Address1,attr => attr.MaximumLength);
ここの答えに触発された... https://stackoverflow.com/a/32501356/324479