[asp.net-mvc] Asp.Net MVC의 DataAnnotations StringLength에서 텍스트 상자의 maxlength 특성

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 개체를 사용하고 원하는 최대 길이 속성을 제공하고 상수를 참조합니다 .. 완전히 정규화 된 네임 스페이스 경로를 통해 상수를 제공해야합니다. .. 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