Asp.Net MVC의 DataAnnotations StringLength에서 텍스트 상자의 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

2
예, 이미 알아 냈습니다. :) 다른 문제가 있지만 데이터 주석은 Model 자체가 아닌 MetaData 클래스에 정의되어 있습니다. 반사가 그들을 포착하지 않습니다!
sabbour

답변 해주셔서 감사합니다. _가 포함 된 속성에 문제가 있습니다. 자동으로-로 변환되지 않습니다. _를 수동으로 바꾸고 새 RouteValueDictionary를 채우는 것과 다른 방법을 알고 있습니까?.
LdN

이 대답은 모델이 속성 인 경우에는 작동하지 않습니다. 예를 들어, 문자열에 대한 편집기 템플릿입니다. 아래 Dave Clemmer의 답변은 모든 경우를 처리합니다.
Michael Brandon Morris

59

눈에 거슬리지 않는 유효성 검사를 사용하는 경우이 클라이언트 측도 처리 할 수 ​​있습니다.

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

귀하의 접근 방식이 나에게 유효성 검사를 제공하는 동안-사용자가 브라우저에 더 많은 문자를 입력하는 것을 방지하고 자바 스크립트가 브라우저에서 실행되는지 여부에 관계없이 작동하기 때문에 실제로 입력에 maxlength 속성을 넣은 후였습니다.
Pervez Choudhury 2012

5
이것이 바로 이것이하는 일입니다. 데이터 최대 길이 유효성 검사 속성을 사용하여 입력 maxlenth 속성을 설정합니다.
jrummell

5
첫 번째 리플렉션 답변에 정말 흥분했지만 복잡한 서버 코드 없이도 동일한 결과를 얻을 수 있습니다. 잘 했어. 더 많은 표를 얻어야합니다.
Brian White

1
+1 Ajaxed 양식에 대한 좋은 아이디어. 나는이 답변이 더 많은 표를받을 자격이 있다는 Brian White의 의견에 동의합니다.
Waleed Eissa

1
자바 스크립트없이 유효성 검사가 필요한 OP에 대한 부분을 놓쳤습니다. 그러나 이것이 다른 사람들이 자바 스크립트 솔루션을 찾는 데 도움이되어 기쁩니다.
jrummell

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 클래스와 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; }

    /* ... */
  }
}

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 개체를 사용하고 원하는 최대 길이 속성을 제공하고 상수를 참조합니다 .. 완전히 정규화 된 네임 스페이스 경로를 통해 상수를 제공해야합니다. .. MyCompany.MyModel.MAX_ZIPCODE_LENGTH .. 모델에서 바로 매달려 있지는 않지만 작동합니다.


2

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);
}

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.