Posts Tagged MVC
ASP.NET MVC: Editor Templates
Posted by Richard de Zwart in .NET, technical on March 27th, 2010
Last week I sat with a colleague who is building an ASP.NET MVC application. Since I’m currently on a not-so-interesting application, I was really jealous and decided to out-smart him, showing off with some hard-core MVC code.
Here we go.
What’s your problem, dude?
Well, we are all very Web 2.0 and so, using Ajax and jQuery and maybe even single-page-applications, but on almost any site I fill in a form, a textbox is just a plain textbox. You can type text, like digits and characters, even if they need only my age. Come-on guys, an age is expressed as a number, so why allow me to type in anything else but digits?
Sure, you validate at the server and maybe even at the client, but still I can make errors that I have to fix later. Please, help me by preventing the error up-front.
The solution, part 1: jQuery-plugin
The solution starts with a nice jQuery-plugin by Paulo P. Marinas called jQuery AlphaNumeric. There is also a nice MaskedInput-plugin, but I found that too limiting.
It is simple to hook it up to my textbox:
$("#mytextboxid").numeric();
And voila, nothing but digits allowed!
Cool, I want that on all the fields that I know are numbers, but I surely don’t want to repeat the above Javascript.
The solution, part 2: EditorFor
Starting with MVC 2 there is an Html-helper called Html.EditorFor(). It uses a lambda as a parameter and derives a lot of interesting information from that. If you type
<%= Html.EditorFor(model => model.Length) %>
You get code generated like this:
<div class="editor-field"> <input id="Length" name="Length" type="text" value="7" /></div>
De ViewEngine determines that you have a Integer field, generates a textbox with the name/id of the field and sets the value. If you have client-side data-validation on, you get the extra span-tag for the validation message. More on that in a minute. Maybe this doesn’t look like much, but you get different output for different field types; like radio-buttons for a Boolean field and appropriate formatting for a Decimal field.
The EditorForModel() method even generates code for all the fields on your class.
The solution, part 3: Turning validation on
I was hoping that the EditorFor() method would be all I needed. It supports validation, both client-side and server-side, and (as argued before) what better client-side validation then preventing false input to begin with!
Alas, it doesn’t do that. But in combination with DataAnnotations (Scott Guthrie has a nice blog about that) it adds range-checks to your Integer-field. So if I can limit the characters that can be typed into my textbox with a line of jQueury and can check for the upper- and lower-limit of the value with client-side validation, then I’m happy!
But that means that I have to change the behavior of the EditorFor() method. Can that be done? Well……, sort of.
The solution, part 4: Convention over configuration
The nice thing about the MVC framework is that it takes convention over configuration, meaning you do not have to configure anything to get a working application, as long as you adhere to the rules/conventions. Nothing new for Ruby on Rails programmers maybe, but for me as a long-time Microsoft-ee it is close to revolutionary.
In this case, the interesting convention is that there can be a EditorTemplates sub-directory in my View directory. If it is in a normal View directory it works for only those Views, but if you put it in the Shared directory it works for all your views:

If you have a user-control in there named Decimal.ascx it will be used everywhere the EditorFor() encounters a Decimal property. There is a bunch of default user-controls that are described by Brad Wilson.
What I would have liked to be “The solution, part 5: Overloading the decimal template”
Since I like the server-side formatting of the default template, and only want to add some jQuery, I would have liked to do the following:
<%@ Control Language="C#" AutoEventWireup="true" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.EditorFor(Model) %> <script type="text/javascript"> $("#mytextboxid").numeric(); </script>
I simply delegate to the original implementation, passing in the data I have available. But that doesn’t work since the EditorFor() expects a lambda as a first parameter. And I don’t know any way to convert the data I have at that point in time to a lambda. If you know, please tell me and you’ll be my hero (hmmmm, I hope it’s not going to be that colleague coming up with the solution, that would really ruin my post…).
The actual “The solution, part 5: Overloading the decimal template”
The best I could do was copying the default template for the Decimal from the blogpost of Brad Wilson. Yes that’s a shame, copying code, having to maintain code that isn’t even mine, not profiting of the improvements Microsoft makes in the default template. Nevertheless, here it is:
<%@ Control Language="C#" AutoEventWireup="true" Inherits="System.Web.Mvc.ViewUserControl" %> <script runat="server"> private object ModelValue { get { if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model) { return String.Format(System.Globalization.CultureInfo.CurrentCulture, "{0:0.00}", ViewData.ModelMetadata.Model); } return ViewData.TemplateInfo.FormattedModelValue; } } </script> <%= Html.TextBox("", ModelValue, new { @class = "text-box single-line" }) %> <script type="text/javascript"> $('#<%= ViewData.ModelMetadata.PropertyName %>').numeric({ allow: '<%= System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator %>'}); </script>
So, there is some server-side code that I copied, and then some client-side code I added myself.
There are two interesting things to mention about my own single line of code:
- The jQuery selector references the name of the property, thus guaranteeing that the javascript is coupled to the right text-box. Other solutions make you use a fixed class or prefix or postfix, but that only opens the possibility of name clashes. The name-property is available in the model-metadata. See the Bradd Wilson series mentioned above for more info
- The allowed character (the decimal separator) for entering money is retrieved from the CurrentCulture and not hard-coded. Of course.
As ScottGu would say: hope this helps.
