Tuesday, October 29, 2013

Custom Textbox Editor Template

ASP.NET MVC's built-in editor template is awesome - by just decorating your model, the Html helper EditorFor knows how to read those attributes and display appropriate html elements and html attributes, including validation, display name, or even using custom templates that we specify through DataType or UIHint attributes.

Now, all my string properties in my models have string length attribute, to prevent truncation when they being saved in the database, or for formatting reason. Now the built-in Html helper EditorFor is not smart enough to translate that StringLength attribute to the actual html attribute of maxlength. Secondly, it is also not smart enough to adjust the width of the textbox based on that StringLength - so a ZipCode property which only need 5 characters should have smaller textbox compared to a CompanyName property which needs 50 characters.

So I made an editor template that do just that. You can copy the code below, put it in a partial view called "string.cshtml" placed within "EditorTemplates" folder under "/Views/Shared/". I use Twitter Bootstrap css to adjust my textbox width (using "input-mini", "input-large" classes) - feel free to substitute them with whatever you are using.
@model String
@using System.Globalization
@using System.Linq
@{
    IEnumerable validators = ModelValidatorProviders.Providers.GetValidators(ViewData.ModelMetadata, ViewContext);
    ModelClientValidationRule stringLengthRule = validators
        .SelectMany(v => v.GetClientValidationRules())
        .FirstOrDefault(m => m.ValidationType == "length");
    if (stringLengthRule != null && stringLengthRule.ValidationParameters.ContainsKey("max"))
    {
        int maxLength = int.Parse(stringLengthRule.ValidationParameters["max"].ToString());
        string inputSize = "input-xxlarge";
        if (maxLength < 5)
        {
            inputSize = "input-mini";
        }
        else if (maxLength < 10)
        {
            inputSize = "input-small";
        }
        else if (maxLength < 30)
        {
            inputSize = "input-medium";
        }
        else if (maxLength < 75)
        {
            inputSize = "input-large";
        }
        else if (maxLength < 150)
        {
            inputSize = "input-xlarge";
        }
        Dictionary attributes = new Dictionary { 
            { "class", inputSize }, 
            { "maxlength", maxLength.ToString(CultureInfo.InvariantCulture) } 
        };
        @Html.TextBox("", Model, attributes)
    }
    else
    {
        Dictionary attributes = new Dictionary { 
            { "class", "input-medium" } 
        };
        @Html.TextBox("", Model, attributes)
    }
}

-- read more and comment ...