Todd Lucas

Software developer, etc.

  • Client Side ModelState

    ASP.NET MVC has good capabilities for handling model state validation. Making use of model attributes, such as Required, it's pretty easy to put together a validating form quickly. MVC 3 introduced unobtrusive validation, which enables client-side validation prior to form post. The HTML helpers render additional mark-up to transparently assist.

    There has been a steady move over the past several years of applications relying more and more on JavaScript. Whereas you would often hear terms like progressive enhancement or graceful degradation, you're now more likely to hear single page app. Libraries like KnockoutJS and AngularJS are becoming very popular.

    A middle-ground approach is to begin moving some server side flows to a more client-first approach. A good candidate for this migration is the simple form. Whereas a typical form might use post/redirect/get, a client-first approach might use a dialog with the embedded form rendered with a partial. If you attempt to move to a more client-first approach, you'll quickly find that much of the validation capability built into MVC is lost.

    Web API 2

    Another great recent advancement in the ASP.NET stack is Web API 2. One of the interesting methods defined for the ApiController base class is BadRequest.

    [HttpPost]
    public IHttpActionResult Post(CheckDatesModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        ...
    }
    

    This method will return any ModelState errors to the client as JSON:

    {
        "Message": "The request is invalid.",
        "ModelState": {
            "model.StartDate": [ "The Start Date field is required." ],
            "model.EndDate": [ "The End Date field is required." ]
        }
    }
    

    Unfortunately, there doesn't appear to be any built-in mechanism to support this data on the client. Regardless, we can make use of this data to do our own client side validation using existing structures. A great example of this is written about in ASP.NET Web API Validation. More on that later.

    Server side

    It would be great if we had a comprehensive approach that would allow us to use the Web API mechanism, or to use a similar mechanism on the MVC side. All we really need are two pieces. The client side piece, for which brette has shown us the way, and a server-side complement to the Web API mechanism for use with MVC. The latter can be accomplished pretty easily using an extension method on the ModelStateDictionary class. Here's an example, written in one line of code :)

    public static class ModelStateDictionaryExtensions
    {
        public static Dictionary<string, string[]> GetErrors(
            this ModelStateDictionary modelState, 
            string prefix)
        {
            return modelState
                .Where(kvp => kvp.Value.Errors.Count > 0)
                .ToDictionary(kvp => String.IsNullOrWhiteSpace(kvp.Key)
                                    ? String.Empty 
                                    : prefix == null 
                                        ? kvp.Key
                                        : prefix.TrimEnd('.') + "." + kvp.Key,
                                kvp => kvp.Value.Errors
                                        .Select(e => e.ErrorMessage)
                                        .ToArray());
        }
    }
    

    To use the extension method from MVC, we would do something like this:

    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult CheckDatesJson(CheckDatesModel model)
    {
        if (!ModelState.IsValid)
        {
            Response.StatusCode = 400;
            return Json(new { 
                Message = "The request is invalid.",
                ModelState = ModelState.GetErrors("model") 
            });
        }
        ...
    }
    

    This produces the identical format as the equivalent Web API method:

    [HttpPost]
    public IHttpActionResult Post(CheckDatesModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        ...
    }
    

    It would be pretty easy to wrap the Json and GetError calls into a method on a base class derived from Controller. One might even call it BadRequest. Although, hopefully, this new version would have a way to override the default Message.

    Client side

    The client side is a bit more complicated. It involves taking the JSON result and applying it to the existing validation mark-up.

    Validation Summary

    One of the first pieces to address is the Html.ValidationSummary() method. This method emits different markup depending on how it's called. The default Html.ValidateSummary() will render all errors, both model-level and errors that are not associated with a model. Other versions allow for the suppression of model-level errors by passing true for the excludePropertyErrors argument. These two versions render different HTML. However, this can be accommodated-for on the client.

    In the default case, Html.ValidationSummary() renders this on initial form render, with the data-valmsg-summary="true" attribute.

    <div class="validation-summary-valid" data-valmsg-summary="true">
        <ul>
            <li style="display:none"></li>
        </ul>
    </div>
    

    After the form post, if there are errors, they will be listed. In addition, the class will change from validation-summary-valid to validation-summary-errors.

    One case that is problematic is when true is passed for excludePropertyErrors. When Html.ValidateSummary(true) is specified, no HTML is emitted on the initial form render. After a form post with errors, the result is the same as the default case, but without the data-valmsg-summary="true" attribute.

    One solution would be to add a wrapper element which will always be present. This is the approach taken here, although it's optional.

    @using (Html.BeginForm()) 
    {
        @Html.AntiForgeryToken()
        <div class="validation-summary">
            @Html.ValidationSummary(true)
        </div>
        ...
    }
    

    This wrapper element allows us to construct validation error mark-up regardless of whether it was emitted by Html.ValidationSummary() or not.

    Ajax and Render

    The last step is to make the Ajax call.

    <script src="~/scripts/app/modelstate.js"></script>
    
    <script>
    $(function () {
        var jForm = $("form");
    
        jForm.submit(function (event) {
            event.preventDefault();
    
            // Clear any previous errors.
            App.ModelState.clearErrors(jForm);
    
            $.ajax({
                url: '/mycontroller/checkdatesjson',
                data: $(this).serializeArray(),
                type: 'POST',
                success: function (data) {
                    // Do something
                },
                statusCode: {
                    400: function (jqXHR) {
                        // Deserialize and render the ModelState.
                        App.ModelState.showResponseErrors(jForm, jqXHR);
                    }
                }
            });
        });
    });
    </script>
    

    The implementation, based on brette's, is written in TypeScript. It can be found on GitHub here.

    Applicability

    This method of moving halfway to client-side processing is best used in places where you still want to use the great features of MVC, but want more client-side interactivity. By using this component, any new client-side form handling can have the same presentation as your existing server-side form handling, allowing for a consistent user experience.

  • Starting Ocam

    I started working on Ocam almost two weeks ago. I've had a need recently for a way of writing documentation for a couple of projects I've been working on. I didn't want to have to write a bunch of server code or to have to install a database to back a blog engine. I wanted something simple and elegant.

    I recently tried out Docco because I had seen it used on a few JavaScript projects and the literate programming attributes had a certain appeal. I used it on my own project and liked the result, so I was looking for something similar.

    In evaluating different documentation systems I like, I've come across several nice examples, including Backbone and Orchard. I was curious about how they approached their documentation. I little research lead me to the OrchardDoc project on GitHub.

    The OrchardDoc project is mainly documentation content, as you might expect. I didn't find much code there. There were a couple of things worth noting, though. The first is that Markdown is the primary means of authoring content. The second is that it requires ASP.NET to run. I liked the Markdown approach, but I was looking for something that didn't have server side dependencies.

    Based on one of the assemblies in the Bin directory, it appeared to use NuGetDocs. A little searching lead to this post which confirmed it. The NuGetDocs project makes interesting use of the MarkdownSharp component to allow Markdown to be used in an ASP.NET environment.

    So here were two two impressive .NET projects and they were both using a Markdown based documentation system. I started to look for alternatives that didn't have the server side dependency.

    Jekyll is probably the best known Markdown site generator. It's used by GitHub and was written by the Cofounder and CTO of GitHub, Tom Preston-Werner. I installed it and played with it a bit. Although it's written in Ruby, it uses Pygments, which is written in Python, for syntax highlighting.

    After using it for a little while, I started wanting to make modifications. Then I got to thinking about how great it would be to have a system like Jekyll that used the idioms that I was familiar with from ASP.NET MVC. In particular, I really like the Razor view engine and their approach to code and markup. I had known about the RazorEngine project, so I started looking into the possibility of writing a stand-alone console app that would have all the features I wanted in a simple package.

    I had been thinking of writing a content platform for some time, but I always abandoned the idea because there isn't a great syntax highlighting package for the .NET platform. The fact that Jekyll called out to Pygments, which is written in a different language, was a kind of permission for me to go forward.

    I uploaded the result about two weeks ago. It's still a work in progress. Please feel free to contact me with questions or comments.