pop·cy·cli·cal

Thursday, December 31, 2009

jQuery Validate and Jeditable, Part 1

I was recently tasked to add server-and-client-side form validation for an ASP.NET MVC site - which already uses in-place editing courtesy Jeditable.  I really like the field editing experience that Jeditable provides – it makes form entry in the browser interactive, is fairly straightforward to integrate, and it’s adaptable to many scenarios.  It does not, however, have any validation mechanism built in. 

Our project already used jQuery Validate for a few forms by using the HTML class definitions – like adding class=”required phone” to an INPUT element.  This works great, but doesn’t provide any server-side validation tie-in.

Earlier this year, I remembered having seen a presentation by Elijah Manor who mentioned using xVal for robust server and client side validation. And with the news that MVC 2 will have a built in validation technique similar to xVal, it was an easy decision to start investigating this library.

xVal is a pretty easy to get integrated.  The first step is to decorate the model with validation rules – I’ve decided to use .NET framework’s DataAnnotations, which ends up looking like:

public class ValidateViewModel
{
    [Required(ErrorMessage = "This string is required")]
    public string StringRequired { get; set; }

    [Range(13, 100, ErrorMessage = "Must be between 13 and 100")]
    public double DoubleRange13_100 { get; set; }
}
public class ValidatedController
{
    public ViewResult Index()
    {
        return View(new ValidateViewModel());
    }
}
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<ValidateViewModel>" %>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<% using(Html.BeginForm()) {%>
    <%= Html.ValidationSummary() %>
    <label for="StringRequired">String Required:</label>
    <%= Html.TextBoxFor(m => m.StringRequired) %>
    <%= Html.ValidationMessageFor(m => m.StringRequired)%>
    
    <label for="DoubleRange13_100">Doulbe between 13 and 100:</label>
    <%= Html.TextBoxFor(m => m.DoubleRange13_100) %>
    <%= Html.ValidationMessageFor(m => m.DoubleRange13_100)%>
<%} %>
</asp:Content>

To validate this ViewModel server side, we use a DataAnnotationsValidationRunner like the one in xVal’s documentation:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Update(ValidateViewModel model)
{
    try {
        DataAnnotationsValidationRunner.ValidateModel(model);
        // it’s valid, do the actual update
        var domainObject = ValidateDomainModel.Find(model.id);
        Map(model, domainObject);
        domainObject.Update();
    }
    catch(RulesException ex) {
        ex.AddModelStateErrors(ModelState, null);
    }

    return ModelState.IsValid ? RedirectToAction("Completed")
                              : (ActionResult) View();
}
public static class DataAnnotationsValidationRunner
{
    private static IEnumerable<ErrorInfo> GetErrors(object instance)
    {
        return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
               from attribute in prop.Attributes.OfType<ValidationAttribute>()
               where !attribute.IsValid(prop.GetValue(instance))
               select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(String.Empty), instance);
    }

    /// <summary>Validates the given <param name="model">model</param></summary>
    /// <exception cref="RulesException">Thrown if any errors are found</exception>
    public static void ValidateModel(object model)
    {
        var errors = GetErrors(model);
        if (errors.Any())
            throw new RulesException(errors);
    }
}

Aside: the project uses the Active record pattern via Castle ActiveRecord for data access without an intermediate business-logic layer.  For this case, the rules are defined on the ViewModel and are validated in the controller.  This does add some noise in the actions – I’m definitely interested in other methods for handling this. Such as - perhaps the validation could be placed inside the ViewModel?

The next step is to add client-side validation.  xVal’s built-in jQuery Validate rule generator makes this ridiculously simple – just reference jquery.validate.js and xVal.jquery.validate.js in the view, and then this single line:

<%= Html.ClientSideValidation<ValidateViewModel>() %>

The rules defined in the ViewModel will now be validated client side and enforced for server side actions.  This works great for a statically defined HTML form, but I learned that integrating with Jeditable’s dynamic inline forms to be not so straight forward.

Continued in Part 2

Comments are closed.