атрибут maxlength текстового поля з DataAnnotations StringLength у Asp.Net MVC


80

Я працюю над додатком MVC2 і хочу встановити атрибути maxlength для вводу тексту.

Я вже визначив атрибут stringlength для об'єкта Model, використовуючи анотації даних, і він правильно перевіряє довжину введених рядків.

Я не хочу повторювати той самий параметр у своїх поданнях, встановлюючи атрибут максимальної довжини вручну, коли модель вже має інформацію. Чи є спосіб зробити це?

Фрагменти коду нижче:

З моделі:

[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 05.03.10

Відповіді:


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' і жодного методу розширення 'TextBoxFor', що приймає перший аргумент типу 'System.Web.Mvc.HtmlHelper <TModel > 'можна знайти (вам не вистачає директиви з використанням або посилання на збірку?) у цьому рядку: return htmlHelper.TextBoxFor <TModel> (вираз, атрибути);
sabbour

2
Так, я це вже зрозумів :) У мене є інша проблема, хоча мої анотації даних визначаються в класах MetaData, а не в самій моделі. Відображення не підхоплює їх!
sabbour

Дякую за відповідь. У мене проблеми з атрибутами, які містять _, вони не перетворюються автоматично на -. Чи знаєте ви інший спосіб, ніж заміна _ вручну та заповнення нового RouteValueDictionary ?.
LdN

Ця відповідь не працює для випадків, коли модель є властивістю. Наприклад, шаблон редактора для рядка. Відповідь Дейва Клеммера розглядає всі випадки.
Майкл Брендон Морріс

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 у браузері.
Первез Чудхурі,

5
Це саме те, що це робить. Він використовує атрибут перевірки максимальної довжини даних, щоб встановити вхідний атрибут maxlenth.
jrummell

5
Мене дуже порадувала перша відповідь на роздуми, але, схоже, для досягнення тих самих результатів без будь-якого складного коду сервера. Хороша робота. Ви повинні отримати більше голосів.
Брайан Уайт

1
+1 Чудова ідея для форм Ajaxed. Я згоден з Брайаном Уайтом, що ця відповідь заслуговує на більшу кількість голосів.
Waleed Eissa

1
Я пропустив частину про ОП, яка потребує перевірки без JavaScript. Але я радий, що це допомогло іншим шукати рішення javascript.
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

Якщо ви хочете, щоб це працювало з класом метаданих, вам потрібно використовувати наступний код. Я знаю, що це не красиво, але це робить роботу і заважає вам писати свої властивості maxlength як у класі Entity, так і в View:

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

Хоча я особисто люблю виправлення jqumel jrummel, ось ще один підхід до збереження єдиного джерела істини у вашій моделі ...

Не гарно, але .. у мене все гаразд ...

Замість того, щоб використовувати прикраси властивостей, я просто визначаю деякі добре названі загальнодоступні константи в моїй бібліотеці моделей / 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.