Link here

Link here

Model-based Client-side Validation for ASP.NET MVC

ASP.NET, MVC, Usability 80 Comments »

For quite a while, I was unsure how to go about doing validation in ASP.NET MVC. By default, it’s incompatible with the ASP.NET <asp:XyzValidator> server controls, so this gives us an opportunity to come up with something newer and better.

There have been a couple of great efforts from the community, including Juergen Baeurle’s MVC Validator Toolkit, and Matt Hawley’s MVC UI Validation Framework (now part of MVC Contrib). I wasn’t really satisfied with either, because they take validation out of the domain model and into the application’s UI (no offense guys, what you’ve done is cool).

So, check out my first ever screencast - 6 thrilling minutes of non-stop coding action, in which you get to add both server-side and breathtakingly fit client-side validation to an ASP.NET MVC application, while keeping all the validation rules right in the domain model. Whoo!

Oh noes! Either your feed reader has removed the screencast, or you haven’t enabled Javascript and Flash. You’re missing out!

For those of you that don’t have time for screencasts (like me, normally), here’s how to get the client-side validation goodness in your ASP.NET MVC application. I’m using Castle Validator for the server-side bits, and Live Validation for the client-side bits, fused into one terrifying alien beast hell-bent on ultimate data purity.

Instructions

1. Download and reference MvcValidator.dll and Castle.Components.Validator.dll (from the Castle project). In your master page, add a reference to the Live Validation script (preferably a local copy of it):

<script src="http://www.livevalidation.com/javascripts/src/1.3/livevalidation_standalone.compressed.js"></script>

2. Add validation attributes to your model object.

public class Person
{
    [ValidateNonEmpty("Please enter a name")]
    public string Name { get; set; }
 
    [ValidateNonEmpty("Please enter an email address")] [ValidateEmail]
    public string Email { get; set; }
 
    [ValidateNonEmpty("Please enter an age")] [ValidateInteger]
    public int? Age { get; set; }
}

3. Drop an Html.ClientSideValidation into your view:

<% using(Html.Form("Home", "SubmitPerson", FormMethod.Post)) { %>
    <p>Name: <%= Html.TextBox("MyPrefix.Name", ViewData.Name) %></p>
    <p>Email: <%= Html.TextBox("MyPrefix.Email", ViewData.Email)%></p>
    <p>Age: <%= Html.TextBox("MyPrefix.Age", ViewData.Age)%></p>
 
    <%= Html.ClientSideValidation(ViewData, s => "MyPrefix." + s) %>
    <%= Html.SubmitButton() %>
<% } %>

4. Strut, pose, be smug. You’re done!

Optional steps:

5. Add server-side validation keyed off the same validation attributes, using Castle Validator’s ValidatorRunner (the screencast shows this in action before the client-side validation).

6. Add CSS rules to get the red/green border effect as shown in the screenshot.

Downloads

Thoughts

I’d really love to see something along these lines baked into the official MVC framework, perhaps following Castle’s example of having a pluggable system for emitting Javascript for particular validation checks, so folks could integrate it with client-side validation libraries of their choice. (In my version, I’ve just hard-coded it to work with Live Validator.)

kick it on DotNetKicks.com

(Server controls vs. Inline code) = (What vs. How)

ASP.NET, MVC 10 Comments »

When each developer first learns about ASP.NET MVC, their immediate reaction to MVC view pages is to be shocked and appalled by the mixture of HTML markup and C# code (”inline code”).

What about separating presentation from logic??” they’ll cry, and of course you’ll explain the bigger picture of separation of concerns: domain logic from application logic from UI logic, and how MVC adds scope for testability and cleaner code and how foreach loops beat databinding any day, and so on, mercilessly kicking WebForms’s elderly butt.

And of course you’re right. But each time I have to type out view code like this:

<table cellspacing="0">
    <thead>
        <tr>
            <th>ID</th>
            <th>Product</th>
            <th>Category</th>
            <th align="right">Price</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <% foreach(var item in ViewData.Products) { %>
            <tr>
                <td><%= item.ProductID %></td>
                <td><%= item.Name %></td>
                <td><%= item.Category %></td>
                <td align="right"><%= item.Price.ToString("c") %></td>
                <td><%= Html.ActionLink("Details", "Show", new { ProductID = item.ProductID }) %></td>
            </tr>
        <% } %>
    </tbody>
</table>

… I can’t help thinking that something’s slightly not right here (and honestly, I try to suppress that dark thought!). Oh don’t get me wrong, I’m a big fan of the new MVC framework, and I’d take it rather than WebForms any day, but software development technology normally progresses like this:

  • Old technology: Specify exactly what you want the computer to do
  • New technology: Specify what end result you want, but let the computer work out the details

Great examples of the latter bullet point are query languages like SQL or even LINQ, and many of C#’s newer features (e.g. type inference). And how about:

  • Classic ASP: Specify exactly what HTML markup you want
  • WebForms: Specify what end result you want (e.g. a grid), and let the framework figure out what HTML markup is required
  • ASP.NET MVC: Um, specify exactly what HTML markup you want

My experience is that WebForms was a great idea, for its time, but in practice it just didn’t work so well. For demoware, it’s an awesome platform, but try to build a 3-person-year project with it and it will all end in tears (but not tiers).

So, with MVC, we think again and take stock of where we are. HTML is still a real concern, WebForms’s abstraction layer *didn’t* save us from browser incompatibilities, and HTTP is still a stateless protocol. Time for a change of plan, back to basics and all that, but do we really have to go back to writing <TABLE><THEAD><TR><TH>… etc.? It’s not hard, it’s just verbose. Do we need a better DSL for HTML? Is that what alternate view engines could provide?

The framework’s built-in HTML helpers really do help us, but they only produce individual HTML tags, not related groups of tags. Maybe we should add some new ones, e.g. Html.Table, so you could just write:

<%= Html.Table(ViewData.Products, new { cellspacing = 0 })
    .AddColumn("ID", i => i.ProductID)
    .AddColumn("Product", i => i.Name)
    .AddColumn("Category", i => i.Category)
    .AddColumn("Price", i => i.Price.ToString("c"), new { align = "right" })
    .AddColumn("", i => Html.ActionLink("Details", "Show", new { ProductID = i.ProductID }))
%>

In case you do think that’s a good idea, here’s the code needed to implement it. Sorry it’s a bit long, but each method has a few overrides to give you more flexibility in how you supply parameters:

namespace StevenSanderson.Mvc.HtmlHelpers
{
    public static class TableHelper
    {
        public static HtmlTable<T> Table<T>(this HtmlHelper html, IEnumerable<T> data)
        {
            return html.Table<T>(data, null);
        }
        public static HtmlTable<T> Table<T>(this HtmlHelper html, IEnumerable<T> data, object htmlAttributes)
        {
            return html.Table<T>(data, htmlAttributes.ToPropertyList());
        }
        public static HtmlTable<T> Table<T>(this HtmlHelper html, IEnumerable<T> data, IDictionary<string, string> htmlAttributes)
        {
            return new HtmlTable<T> { Data = data, HtmlAttributes = htmlAttributes };
        }
 
        public class HtmlTable<T>
        {
            public IEnumerable<T> Data; 
            public IDictionary<string, string> HtmlAttributes;
            private IList<HtmlTableColumn> columns = new List<HtmlTableColumn>();
 
            public HtmlTable<T> AddColumn(string headerText, Func<T, object> itemText)
            {
                return this.AddColumn(headerText, itemText, null);
            }
 
            public HtmlTable<T> AddColumn(string headerText, Func<T, object> itemText, object htmlAttributes)
            {
                return this.AddColumn(headerText, itemText, htmlAttributes.ToPropertyList());
            }
 
            public HtmlTable<T> AddColumn(string headerText, Func<T, object> itemText, IDictionary<string, string> htmlAttributes)
            {
                columns.Add(new HtmlTableColumn
                {
                    HeaderText = headerText,
                    ItemText = itemText,
                    HtmlAttributes = htmlAttributes
                });
                return this;
            }
 
            private void RenderAttributes(HtmlTextWriter writer, IDictionary<string, string> attribs)
            {
                if (attribs != null)
                    foreach (var attrib in attribs)
                        writer.AddAttribute(attrib.Key, attrib.Value.ToString(), true);
            }
 
            public override string ToString()
            {
                using (StringWriter sw = new StringWriter())
                {
                    HtmlTextWriter writer = new HtmlTextWriter(sw);
 
                    // <TABLE>
                    RenderAttributes(writer, this.HtmlAttributes);
                    writer.RenderBeginTag(HtmlTextWriterTag.Table);
 
                    // Headers
                    writer.RenderBeginTag(HtmlTextWriterTag.Thead);
                    RenderTableRow(writer, HtmlTextWriterTag.Th, col => col.HeaderText);
                    writer.RenderEndTag(); // </THEAD>
 
                    // Rows
                    writer.RenderBeginTag(HtmlTextWriterTag.Tbody);
                    foreach (T row in Data)
                        RenderTableRow(writer, HtmlTextWriterTag.Td, col => col.ItemText(row).ToString());
                    writer.RenderEndTag(); // </TBODY>
 
                    writer.RenderEndTag(); // </TABLE>
                    return sw.ToString();
                }
            }
 
            private void RenderTableRow(HtmlTextWriter writer, HtmlTextWriterTag cellTag, Func<HtmlTableColumn, string> cellValue)
            {
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                foreach (var col in columns)
                {
                    RenderAttributes(writer, col.HtmlAttributes);
                    writer.RenderBeginTag(cellTag);
                    writer.Write(cellValue(col));
                    writer.RenderEndTag(); // </TH>
                }
                writer.RenderEndTag(); // </TR>
            }
 
            class HtmlTableColumn
            {
                public string HeaderText { get; set; }
                public Func<T, object> ItemText { get; set; }
                public IDictionary<string, string> HtmlAttributes { get; set; }
            }
        }
 
        private static IDictionary<string, string> ToPropertyList(this object obj)
        {
            return obj == null ? null
                : obj.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(obj, null).ToString());
        }
    }
}

Note that, to use this, you need to add <%@ Import Namespace=”StevenSanderson.Mvc.HtmlHelpers” %> to the top of your view page (probably changing the namespace to something more suitable for your project), or add the namespace to your web.config in the system.web/pages/namespaces node.

Site Meter