Introduction
If we want to present dates, phone numbers, etc., you would like them to enter the data in a certain format. You can use easily jquery to mask the input, but the only drawback to this approach is that we need to add those functions for each input to be masked.
In MVC 3/ MVC 4, we can easily achieve this by creating custom data annotation and use it on model.
Background
When I am searching on the Internet, I found nothing which provided Input Masking using data annotation. Only this post provided me with some idea. So, I am working on this and solved the problem.
I am using Masked Input Plugin to mask my input.
Using the Code
First of all, we need to create our own Custom Attribute class.
public class InputMaskAttribute : Attribute, IMetadataAware
{
private string _mask = string.Empty;
public InputMaskAttribute(string mask)
{
_mask = mask;
}
public string Mask
{
get { return _mask; }
}
private const string ScriptText = "<script type='text/javascript'>" +
"$(document).ready(function () {{" +
"$('#{0}').mask('{1}');}});</script>";
public const string templateHint = "_mask";
private int _count;
public string Id
{
get { return "maskedInput_" + _count; }
}
internal HttpContextBase Context
{
get { return new HttpContextWrapper(HttpContext.Current); }
}
public void OnMetadataCreated(ModelMetadata metadata)
{
var list = Context.Items["Scripts"]
as IList<string> ?? new List<string>();
_count = list.Count;
metadata.TemplateHint = templateHint;
metadata.AdditionalValues[templateHint] = Id;
list.Add(string.Format(ScriptText, Id, Mask));
Context.Items["Scripts"] = list;
}
}
Here is the screenshot:
Now, we have to create an extension of View Data.
public static TAttribute GetModelAttribute<TAttribute>
(this ViewDataDictionary viewData, bool inherit = false) where TAttribute : Attribute
{
if (viewData == null) throw new ArgumentException("ViewData");
var containerType = viewData.ModelMetadata.ContainerType;
return
((TAttribute[])
containerType.GetProperty(viewData.ModelMetadata.PropertyName).
GetCustomAttributes(typeof(TAttribute),
inherit)).
FirstOrDefault();
}
and to call the JavaScript, we will implement a method extension called RenderScripts
.
public static IHtmlString RenderScripts(this HtmlHelper htmlHelper)
{
var scripts = htmlHelper.ViewContext.HttpContext.Items["Scripts"] as IList<string>;
if (scripts != null)
{
var builder = new StringBuilder();
foreach (var script in scripts)
{
builder.AppendLine(script);
}
return new MvcHtmlString(builder.ToString());
}
return null;
}
Now, we have successfully created a custom attribute. Now, create a Model
/ViewModel
class and use the newly created attribute.
public class TestModel
{
[Display(Name = "Date")]
[InputMask("99/99/9999")]
public DateTime Date { get; set; }
[Display(Name = "Phone")]
[InputMask("(999) 999-9999")]
public string Phone { get; set; }
[Display(Name = "Phone + Ext")]
[InputMask("(999) 999-9999? x99999")]
public string Phone_Ext { get; set; }
[Display(Name = "TaxID")]
[InputMask("99-9999999")]
public string TaxID { get; set; }
[Display(Name = "SSN")]
[InputMask("999-99-9999")]
public string SSN { get; set; }
[Display(Name = "ProductKey")]
[InputMask("a*-999-a999")]
public string ProductKey { get; set; }
[Display(Name = "IP")]
[InputMask("999.999.999.999")]
public string IP { get; set; }
}
Create a strongly typed view:
<legend>Input Masking</legend>
<ol>
<li>@Html.EditorFor(m=> m.Date)
</li>
<li>@Html.EditorFor(m=> m.Phone)
</li>
<li>@Html.EditorFor(m=> m.Phone_Ext)
</li>
<li>@Html.EditorFor(m=> m.TaxID)
</li>
<li>@Html.EditorFor(m=> m.SSN)
</li>
<li>@Html.EditorFor(m=> m.ProductKey)
</li>
<li>@Html.EditorFor(m=> m.IP)
</li>
</ol>
In ~/Views/Shared/EditorTemplates, add this:
@model String
@{ var maskedInput = ViewData.GetModelAttribute<InputMaskAttribute>();
if (maskedInput != null)
{
<div class="editor-label">
@Html.LabelForModel()
</div>
<div class="editor-field">
@Html.TextBoxFor(m => m, new
{ id = ViewData.ModelMetadata.AdditionalValues[InputMaskAttribute.templateHint] })
</div>
}
}
You are almost done. Just do one thing, add reference of Masked Input Plugin.
@section Scripts
{
<script src="~/Scripts/InputMask.js"></script>
@Html.RenderScripts()
}
Conclusion
Here is the output: