thuộc tính maxlength của một hộp văn bản từ DataAnnotations StringLength trong Asp.Net MVC


80

Tôi đang làm việc trên một ứng dụng MVC2 và muốn đặt thuộc tính maxlength của đầu vào văn bản.

Tôi đã xác định thuộc tính stringlength trên đối tượng Model bằng cách sử dụng chú thích dữ liệu và nó đang xác thực độ dài của các chuỗi đã nhập một cách chính xác.

Tôi không muốn lặp lại cài đặt tương tự trong các chế độ xem của mình bằng cách đặt thuộc tính chiều dài tối đa theo cách thủ công khi mô hình đã có thông tin. Có cách nào để làm điều này?

Các đoạn mã bên dưới:

Từ mô hình:

[Required, StringLength(50)]
public string Address1 { get; set; }

Từ Chế độ xem:

<%= Html.LabelFor(model => model.Address1) %>
<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long" })%>
<%= Html.ValidationMessageFor(model => model.Address1) %>

Điều tôi muốn tránh làm là:

<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long", maxlength="50" })%>

Tôi muốn nhận đầu ra này:

<input type="text" name="Address1" maxlength="50" class="text long"/>

Có cách nào để làm điều này?


Tôi xin lỗi, tôi không biết Thông báo dữ liệu có tác dụng gì? Ý tôi là, nếu tiêu chí độ dài thay đổi thì sao? Điều này có thể không được điều khiển động (trong thời gian chạy), dựa trên một số siêu dữ liệu?
shahkalpesh

Câu trả lời:


51

Tôi không biết có cách nào để đạt được điều này mà không cần đến sự suy ngẫm. Bạn có thể viết một phương thức trợ giúp:

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

mà bạn có thể sử dụng như thế này:

<%= Html.CustomTextBoxFor(model => model.Address1, new { @class = "text long" })%>

Tôi đang gặp lỗi 1 'System.Web.Mvc.HtmlHelper <TModel>' không chứa định nghĩa cho 'TextBoxFor' và không có phương thức mở rộng 'TextBoxFor' chấp nhận đối số đầu tiên của loại 'System.Web.Mvc.HtmlHelper <TModel > 'có thể được tìm thấy (bạn có thiếu một chỉ thị đang sử dụng hoặc một tham chiếu hợp ngữ?) tại dòng này: return htmlHelper.TextBoxFor <TModel> (biểu thức, thuộc tính);
sabbour

2
Vâng, tôi đã tìm ra điều đó rồi :) Tuy nhiên, tôi có một vấn đề khác, các chú thích dữ liệu của tôi được xác định trên các lớp MetaData chứ không phải là chính Mô hình. Sự phản chiếu không chọn chúng!
sabbour

Cảm ơn vì câu trả lời. Tôi đang gặp sự cố với các thuộc tính chứa _, chúng không được tự động chuyển đổi thành -. Bạn có biết cách nào khác ngoài việc thay thế _ theo cách thủ công và điền vào một RouteValueDictionary mới không?
Juan

Câu trả lời này không hoạt động đối với trường hợp mô hình là thuộc tính. Ví dụ, một mẫu trình soạn thảo cho một chuỗi. Câu trả lời của Dave Clemmer dưới đây xử lý mọi trường hợp.
Michael Brandon Morris

59

Nếu bạn đang sử dụng xác thực không phô trương, bạn cũng có thể xử lý phía máy khách này:

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

Trong khi cách tiếp cận của bạn sẽ cung cấp cho tôi sự xác thực - tôi thực sự đã đặt thuộc tính maxlength vào đầu vào vì nó sẽ ngăn người dùng nhập thêm ký tự vào trình duyệt và sẽ hoạt động bất kể javascript đang chạy trong trình duyệt.
Pervez Choudhury

5
Đó chính xác là những gì điều này làm. Nó sử dụng thuộc tính xác thực độ dài tối đa của dữ liệu để đặt thuộc tính maxlenth đầu vào.
jrummell

5
Tôi thực sự phấn khích với câu trả lời phản ánh đầu tiên, nhưng điều này có vẻ đạt được kết quả tương tự mà không cần bất kỳ mã máy chủ phức tạp nào. Làm tốt lắm. Bạn sẽ nhận được nhiều phiếu bầu hơn.
Brian White

1
+1 Ý tưởng tuyệt vời cho các biểu mẫu Ajaxed. Tôi đồng ý với Brian White rằng câu trả lời này xứng đáng được bình chọn hơn.
Waleed Eissa

1
Tôi đã bỏ lỡ phần về OP cần xác thực mà không cần javascript. Nhưng tôi rất vui vì điều này đã giúp những người khác đang tìm kiếm giải pháp javascript.
jrummell

20

Tôi sử dụng CustomModelMetaDataProvider để đạt được điều này

Bước 1. Thêm lớp CustomModelMetadataProvider mới

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

Bước 2. Trong Global.asax, Đăng ký CustomModelMetadataProvider

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
    ModelMetadataProviders.Current = new CustomModelMetadataProvider();
}

Bước 3. Trong Views / Shared / EditorTemplates Thêm một phần xem có tên là 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  })%>
<% } %>

Làm xong...

Biên tập. Bước 3 có thể bắt đầu trở nên tồi tệ nếu bạn muốn thêm nhiều thứ hơn vào hộp văn bản. Nếu đây là trường hợp của bạn, bạn có thể làm như sau:

<%@ 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

Nếu bạn muốn điều này hoạt động với một lớp siêu dữ liệu, bạn cần sử dụng mã sau. Tôi biết nó không đẹp nhưng nó hoàn thành công việc và ngăn bạn phải viết các thuộc tính maxlength của mình trong cả lớp Thực thể và Chế độ xem:

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

Lớp mẫu :

[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

Mặc dù cá nhân tôi yêu thích bản sửa lỗi jquery của jrummel, đây là một cách tiếp cận khác để duy trì một nguồn chân lý duy nhất trong mô hình của bạn ...

Không đẹp, nhưng .. đã làm việc tốt cho tôi ...

Thay vì sử dụng các trang trí thuộc tính, tôi chỉ định nghĩa một số hằng số công khai được đặt tên tốt trong thư viện / dll mô hình của tôi, và sau đó tham chiếu chúng trong chế độ xem của tôi thông qua HtmlAttributes, ví dụ:

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

Sau đó, trong tệp dạng xem dao cạo, trong EditorFor ... sử dụng một đối tượng HtmlAttirubte trong tình trạng quá tải, cung cấp thuộc tính chiều dài tối đa mong muốn và tham chiếu đến hằng số .. bạn sẽ phải cung cấp hằng số thông qua một đường dẫn không gian tên đủ điều kiện. .. MyCompany.MyModel.MAX_ZIPCODE_LENGTH .. vì nó sẽ không bị treo ngay khỏi mô hình, nhưng, nó hoạt động.


2

Tôi nhận thấy cách tiếp cận dựa trên phản xạ của Darin đặc biệt hữu ích. Tôi thấy rằng sử dụng siêu dữ liệu ContainerTypelàm cơ sở để lấy thông tin thuộc tính đáng tin cậy hơn một chút , vì phương thức này có thể được gọi trong các mẫu hiển thị / trình chỉnh sửa mvc (nơi TModelcuối cùng là một loại đơn giản chẳng hạn 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

Dưới đây là một số phương thức tĩnh bạn có thể sử dụng để lấy StringLength hoặc bất kỳ thuộc tính nào khác.

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

}

Sử dụng phương thức tĩnh ...

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

Hoặc sử dụng phương thức mở rộng tùy chọn trên một phiên bản ...

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

Hoặc sử dụng phương thức tĩnh đầy đủ cho bất kỳ thuộc tính nào khác ...

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

Lấy cảm hứng từ câu trả lời ở đây ... https://stackoverflow.com/a/32501356/324479

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.