your programing

Asp.Net MVC의 DataAnnotations StringLength에있는 텍스트 상자의 maxlength 특성

lovepro 2020. 10. 14. 08:14
반응형

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

    /* ... */
  }
}

While I'm personally loving jrummel's jquery fix, here's another approach to keeping a single-source-of-truth up in your model...

Not pretty, but.. has worked o.k. for me...

Instead of using property decorations, I just define some well-named public constants up in my model library/dll, and then reference them in my view via the HtmlAttributes, e.g.

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

Then, in the razor view file, in the EditorFor... use an HtmlAttirubte object in the overload, supply the desired max-length property and referenece the constant.. you'll have to supply the constant via a fully qualied namespace path... MyCompany.MyModel.MAX_ZIPCODE_LENGTH.. as it won't be hanging right off the model, but, it works.


I found Darin's reflection based approach to be especially helpful. I found that it was a little more reliable to use the metadata ContainerType as the basis to get the property info, as this method can get called within mvc editor/display templates (where TModel ends up being a simple type such as 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);
}

Here are some static methods you can use to get the StringLength, or any other attribute.

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

}

Using the static method...

var length = AttributeHelpers.GetStringLength<User>(x => x.Address1);

Or using the optional extension method on an instance...

var player = new User();
var length = player.GetStringLength(x => x.Address1);

Or using the full static method for any other attribute...

var length = AttributeHelpers.GetPropertyAttributeValue<User,string,StringLengthAttribute,Int32>(prop => prop.Address1,attr => attr.MaximumLength);

Inspired by the answer here... https://stackoverflow.com/a/32501356/324479

참고URL : https://stackoverflow.com/questions/2386365/maxlength-attribute-of-a-text-box-from-the-dataannotations-stringlength-in-asp-n

반응형