Asp.NetMVCのDataAnnotationsStringLengthからのテキストボックスのmaxlength属性


80

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"/>

これを行う方法はありますか?


申し訳ありませんが、データアノネーションが何に適しているのかわかりませんか?つまり、長さの基準が変更された場合はどうなりますか?一部のメタデータに基づいて、これを動的に(実行時に)駆動することはできませんか?
shahkalpesh 2010年

回答:


51

私は、反省に頼らずにこれを達成する方法を知りません。ヘルパーメソッドを書くことができます:

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" })%>

エラー1が発生します。「System.Web.Mvc.HtmlHelper <TModel>」に「TextBoxFor」の定義が含まれておらず、「System.Web.Mvc.HtmlHelper <TModel」タイプの最初の引数を受け入れる拡張メソッド「TextBoxFor」がありません。 > 'は次の行にあります(usingディレクティブまたはアセンブリ参照がありませんか?):return htmlHelper.TextBoxFor <TModel>(expression、attributes);
sabbour 2010年

2
ええ、私はすでにそれを理解しました:)しかし、私は別の問題を抱えています、私のデータ注釈はモデル自体ではなくメタデータクラスで定義されています。反射はそれらを拾っていません!
sabbour 2010年

答えてくれてありがとう。_を含む属性に問題があり、自動的に-に変換されません。_を手動で置き換えて新しいRouteValueDictionaryに入力するのとは別の方法を知っていますか?
ldN 2015

この回答は、モデルがプロパティである場合には機能しません。たとえば、文字列のエディタテンプレート。以下のDaveClemmerの回答は、すべてのケースを処理します。
Michael

59

目立たない検証を使用している場合は、このクライアント側も処理できます。

$(document).ready(function ()
{
    $("input[data-val-length-max]").each(function ()
    {
        var $this = $(this);
        var data = $this.data();
        $this.attr("maxlength", data.valLengthMax);
    });
});

あなたのアプローチは私に検証を与えますが、私は実際に入力にmaxlength属性を入れた後です。なぜなら、それはユーザーがブラウザーにそれ以上の文字を入れるのを防ぎ、javascriptがブラウザーで実行されているかどうかに関係なく機能するからです。
Pervez Choudhury 2012

5
それはまさにこれが行うことです。データの最大長検証属性を使用して、入力maxlenth属性を設定します。
jrummell 2012

5
私は最初のリフレクションの答えに本当に興奮しましたが、これは複雑なサーバーコードなしで同じ結果を達成するように見えます。よくやった。あなたはより多くの票を獲得する必要があります。
ブライアンホワイト

1
+ 1Ajaxedフォームの素晴らしいアイデア。私は、この回答がより多くの票に値するというブライアン・ホワイトに同意します。
Waleed Eissa 2012

1
私はJavaScriptなしで検証を必要とするOPについての部分を見逃しました。しかし、これが他の人がjavascriptソリューションを探すのに役立ったことをうれしく思います。
jrummell 2012

20

これを実現するために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)%>

8

これをメタデータクラスで機能させるには、次のコードを使用する必要があります。私はそれがきれいではないことを知っていますが、それは仕事を成し遂げ、あなたがEntityクラスとビューの両方であなたの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; }

    /* ... */
  }
}

3

私は個人的に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 ..モデルからすぐにぶら下がらないため、機能します。


2

ダリンのリフレクションベースのアプローチが特に役立つことがわかりました。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);
}

1

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.