maxlength атрибут текстового поля из DataAnnotations StringLength в Asp.Net MVC
Я работаю над приложением MVC2 и хочу установить атрибуты maxlength текстовых входов.
Я уже определил атрибут stringlength для объекта модели, используя аннотации данных, и он правильно проверяет длину введенных строк.
Я не хочу повторять ту же настройку в моих представлениях, устанавливая атрибут max length вручную, когда модель уже имеет информацию. Есть ли способ сделать это?
код фрагменты ниже:
модель:
[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"/>
есть ли способ сделать это?
7 ответов:
Я не знаю никакого способа достичь этого, не прибегая к рефлексии. Вы можете написать вспомогательный метод:
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. В Глобальном.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)%>
Если вы хотите, чтобы это работало с классом метаданных, вам нужно использовать следующий код. Я знаю, что это не очень красиво, но он выполняет свою работу и не позволяет вам писать свои свойства 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; } /* ... */ } }
хотя я лично люблю jQuery-исправление jrummel, вот еще один подход к сохранению одного источника истины в вашей модели...
Не очень, но.. работал o. k.для меня...
вместо того, чтобы использовать украшения свойств, я просто определяю некоторые хорошо названные публичные константы в моей библиотеке моделей / 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
затем, в файле представления razor, в EditorFor... используйте HtmlAttirubte объект в перегрузке, поставьте пожеланное свойство max-length и referenece константа.. вы должны будете предоставить константу через полностью квалифицированный путь пространства имен... Название_компании.MyModel.MAX_ZIPCODE_LENGTH.. поскольку он не будет висеть прямо с модели, но он работает.
Я нашел подход, основанный на отражении Дарина, особенно полезным. Я обнаружил, что это было немного более надежным, чтобы использовать метаданные
ContainerType
в качестве основы для получения информации о свойстве, так как этот метод может вызываться в шаблонах MVC editor/display (где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