Here the property in my ViewModel:
[Display(Name = "Ext.")]
[MaxLength(6, ErrorMessage = "Must be a maximum of 6 characters")]
[StringLength(6)]
public string Extension { get; set; }
And in my View:
@Html.EditorFor(model => model.Extension)
And it renders:
<input class="text-box single-line" data-val="true" data-val-length="The field Ext. must be a string with a maximum length of 6." data-val-length-max="6" id="Extension" name="Extension" type="text" value="" />
Should this be setting the maxlength attribute on my textbox? If not, how can I do that with DataAttributes?
I'd like the attribute that I set in the ViewModel to control this if possible.
ASP.NET MVC provides an extensible system for doing exactly this. Here is what you need to do:
ModelMetadataProvider
.StringLengthAttribute
or the MaxLengthAttribute
, extract the information and add it to the ModelMetadata
.Step 1: Implement a custom ModelMetadataProvider
.
Create a class that derives from ModelMetadataProvider
. Typically you would derive from the DataAnnotationsModelMetadataProvider
as this provides some default functionality which means you only have to override a single method called CreateMetadata
.
Step 2: Extract the information:
To get the information, you need to look for the attribute, extract the maximum length information and add it to the AdditionalValues
dictionary of the ModelMetadata
. The implementation would look something like this (this is the entire implementation):
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
// Call the base class implementation to create the default metadata...
var metadata = base.CreateMetadata(
attributes,
containerType,
modelAccessor,
modelType,
propertyName);
// Extract the stringLengthAttribute (you can do the same for the
// MaxLengthAttribute if you want).
var attr = attributes
.OfType<StringLengthAttribute>()
.First();
// This could be getting called on a property that doesn't have the
// attribute so make sure you check for null!
if (attr != null)
{
metadata.AdditionalValues["maxLength"] = attr.MaximumLength;
}
return metadata;
}
}
In order for ASP.NET MVC to use this you need to register it in the Application_Start
method in Global.asax
.
ModelMetadataProviders.Current = new CustomModelMetadataProvider();
Step 3: Create a custom editor template.
You now need to create a view that uses the information. Create a new view called String
in the Views\Shared\
folder.
String.cshtml
@{
object maxLength;
if (!ViewData.ModelMetadata.AdditionalValues
.TryGetValue("maxLength", out maxLength))
{
maxLength = 0;
}
var attributes = new RouteValueDictionary
{
{"class", "text-box single-line"},
{ "maxlength", (int)maxLength },
};
}
@Html.TextBox("", ViewContext.ViewData.TemplateInfo.FormattedModelValue, attributes)
When you run your application you will get the following HTML output by calling @Html.EditorFor
.
<input class="text-box single-line" id="Extension" maxlength="6" name="Extension" type="text" value="" />
If you want to know more about the model metadata provider system, Brad Wilson has a series of blog posts that detail how it works (these were written prior to the Razor view engine so some of the view Syntax is a bit funky but otherwise the information is sound).
Essentially based on Brad's answer, wrapped in an extension on the Html helper using lambda syntax so you don't pollute your Razor views with reflection stuff:
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
public static class HtmlHelper
{
public static int? MaxLength<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
MemberExpression memberExpression = (MemberExpression)expression.Body;
PropertyInfo property = typeof(TModel)
.GetProperty(memberExpression.Member.Name);
StringLengthAttribute attribute = (StringLengthAttribute)property
.GetCustomAttributes(typeof(StringLengthAttribute), true)
.FirstOrDefault();
if (attribute != null)
{
return attribute.MaximumLength;
}
return null;
}
}
Use it like such:
@Html.TextBoxFor(x => x.Name, new { maxlength = Html.MaxLength(x => x.Name) })
where x
refers to your model.
If the StringLengthAttribute
is not declared for the property, null
will be returned and the maxlength
attribute will be empty on the textbox element.
Remember to include using in your razor page so you can access the method.
@using HtmlHelper
You also need to use none null-able result for the method to overcome compile error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With