Skip to content

A Better View API for Grids in ASP.NET MVC

I’m writing a grid-independent interface for displaying data in ASP.NET MVC applications, and I would like your feedback on the API design.

In my last post, I discussed some of the problems with existing grid components for ASP.NET MVC. Actually, there are a couple more design issues which I forgot to mention in that post. I’ll discuss them briefly before talking about View design.

  • Many grids require two requests in order to display the first page of data: One for the page itself, then a second, AJAX request for the data. I can understand why the AJAX request is necessary for the second page of grid data, but shouldn’t the first page just come already populated? Making too many requests is one of the most common causes of performance problems in web applications.
  • Many grids require executing a JavaScript method in order to set up the component. Unfortunately, they unnecessarily wait until the jQuery.ready event fires, resulting in unnecessary visual disruption and poor performances the page loads. It’s often unnecessary to wait for the ready event, so you can visibly improve the perceived performance of the page by simply calling that method sooner!
  • The two problems above compound each other. If you wait until the ready event fires to even issue a request for data, and then wait some more for that data to return, that can be a lot of waiting!

Displaying Models in MVC 2

In ASP.NET MVC 1, you had to write specific markup in each view, in order to display each individual property of the view model correctly.

In ASP.NET MVC 2, there is a standard way to render the entire model for your page, no matter what kind of data you plan to show:

<%: Html.DisplayForModel() %>

When you do this, MVC will look up the correct template for your model, or its sub-properties, based on its type, and render it appropriately. You can easily customize this with user controls, at any level of your application. You can read about the details on Brad Wilson’s blog.

(Unfortunately, MVC 2’s VS tooling does not use this pattern when it generates new views. I believe this is fixed in MVC 3.)

If you want to render only a certain property of the view model, rather than the entire model (perhaps you have other properties on the view model which control page layout, and are not intended for the final, rendered page itself) then you use:

<%: Html.DisplayFor(m => m.MyCoolGrid) %>

Only if you want to render a control with customizations which cannot be inferred from the metadata on your view model should it be necessary to specify a specific control:

<%: Html.Grid(Model.MyCoolGrid, new { @class = "importantGrid" } ) %>

A Better Grid View API

Oddly, the demo code for most grids supporting MVC seems to favor the third form over the first two. To me, the third form is the exception, and the first two should be the standard methods for most pages. So the "ideal grid API" for views is really no API at all; you simply use the built-in methods which are already favored by MVC for every other kind of model.

Namespace Issues

In a non-trivial MVC application, it is very easy to go overboard with extensions to HtmlHelper. Some components, like Telerik’s suite for ASP.NET MVC, fight this by adding a single extension method, and then putting their components into this "namespace":

<%: Html.Telerik().Grid(Model) %>

I think that this attempt to fight clutter is admirably intentioned, but probably unnecessary. If you’re only going to use Telerik’s controls on a single page, then you can easily Import that namespace on just that one page, instead of specifying it in web.config. If, on the other hand, you intend to use them in most pages in your application, then the extra .Telerik() call in the code above is just noise. Yes, existing code might have a Grid extension method for HtmlHelper, but it is unlikely to specify Telerik’s model as its first argument, so it should not actually conflict.

So, in my initial design, I’ve opted to not do this. However, I remain open to arguments as to why I should. There doesn’t seem to be any clear right or wrong.

Custom Options

Of course, sometimes you do need to use the third form, because the model supplied by the controller is not enough to totally specify the custom needs of a particular page. MVC’s convention for this appears to be to supply large numbers of different overloads for each control, representing every possible combination of options imaginable. (There are, for example, 10 different overloads of Html.ActionLinkcausing no small amount of confusion to MVC developers.)

Instead of overloads, I decided to use a strongly-typed options object. This requires a few more keystrokes, but the resulting code is much more readable, and, as I noted above, even calling the Grid method directly at all is the exception rather than the rule.

Here, for example, is how you might choose to override default options for one specific grid:

<%: Html.Grid(Model, new GridViewOptions
                         {
                             Multiselect = true,
                             Pager = false
                         } ) %>

This is much easier to understand than:

<%: Html.Grid(Model, 18, false, "Foo!" ) %>

I can also change the rendered grid to something other than the default grid for the project:

<%: Html.Grid(Model, new GridViewOptions
                         {
                             Renderer = new JqGridRenderer()
                         } ) %>

In many cases, this will be the only code change necessary in order to display a different grid, despite the fact that, for example, plain HTML tables and JqGrids work very differently!

What’s Not There

Please note that there is no need to define grid columns, headers, alignment, etc., in the view. This can all be inferred from model metadata!

A Fluent Interface?

Another possible approach, which some people like, is to use a so-called "fluent" interface. Telerik does this for their grid. With this approach, my example above would look like:

<%= Html.Grid(Model).EnableMultiselect().NoPager() %>

Note carefully, however, that I had to change the "safe" <%: syntax introduced in .NET 4 to the old, "unsafe" <%= syntax from .NET 2. The code above only works because the .ToString() method on the returned type is overridden to return markup — as a string, not as an MvcHtmlString! The <%: syntax handles encoding strings automatically, and, in my opinion, the <%= syntax should not be used at all in .NET 4. A "beautiful" API which requires using features which make your site vulnerable to XSS attacks is not a step forward, in my opinion!

I can’t think of a way to make the "fluent" approach work with the .NET 4 syntax. The Grid method is going to return HTML markup, so it should not be encoded. However, the "fluent" interface requires returning a type containing the fluent methods, which will not be MvcHtmlString. So a call like this:

<%: Html.Grid(Model).EnableMultiselect().NoPager() %>

…would return encoded output (e.g., "&lt;table&gt;"); not what we want! And this:

<%: Html.Grid(Model).EnableMultiselect().NoPager().ToHtmlString() %>

…is just ugly! If the user forgot the .ToHtmlString() bit, they would get no error, but get encoded output at runtime. Yuck!

I don’t want to close the door to a "fluent" interface permanently, since some people like them, and those who do not like them would not need to use it, but I’m not going to write it unless I can find a way to do it well, and I haven’t been impressed by what I’ve seen in other projects. If you’re reading this and you’re aware of a technique for overcoming the issues I’ve described here, please enlighten me!

The Part with the JavaScript

Many grids require calling a JavaScript method, often with a big JavaScript object specifying the configuration of the grid. Now, one of the original tenets of ASP.NET MVC, from day one, was "control over every angle bracket in your page."

Unfortunately, this dictum has not always been followed when it comes to the generated JavaScript. MVC 2’s validation support, for example, injects its script into the middle of your page, and you cannot change this. (MVC 3, however, fixes this.)

I have seen commercial grid components for MVC which inject a giant $.ready event handler right into the middle of the rendered markup! I’m not going to name names, but before you pay for a commercial grid, look at the markup on their demo site! In fairness, there are also commercial grid components which handle this better.

Anyway, in addition to an HtmlHelper extension for the HTML markup, we also need an extension for including the necessary JavaScript (if any; a "plain HTML table" grid renderer might not require any JavaScript at all) references in the rendered page:

<%: Html.GridScriptTag() %>

What this method really means is, "If any of the grids I have included in the page thus far require JavaScript code, please put that script right here." Because this method has a fairly low overhead and emits no markup if you haven’t actually used any grids on the page, you can put it in your Site.Master, if you prefer. Importantly, however, you can also put it somewhere else. This allows you to optimize the point at which the JavaScript method is invoked, should you need to do that.

This method has an overload which takes a strongly-typed options object. You won’t typically need to use this, but you might have JavaScript which you would like to include if and only if there is at least one grid present on the page:

<%: Html.GridScriptTag(new ViewOptions
                           {
                               AfterGridInitializationJavaScript = SomeScript
                           }) %>

"Unobtrusive" Scripting?

MVC 3 has an "unobtrusive" client validation option. Would this even make sense for a grid? I haven’t figured this out yet, but I’m thinking about it.

How Am I Doing?

I’m afraid that’s quite a lot of discussion for what is, in the end, design concerns over the signatures of two methods. If you’re still reading this, thanks!

At the same time, however, I really want to get this right. The controller and view APIs of this library are the parts which I imagine developers will have to grapple with day in and day out. If the internals of the library aren’t pretty in every corner, well, nothing is perfect. But the controller and view APIs are extremely visible, so they deserve a lot of scrutiny!

What’s Next?

In the future, I’d like to similarly examine controller design. But first I have some questions about testing this stuff; it’s tricky! Oh, yes, and I’ll be publishing the code!

{ 3 } Comments

  1. Alexandre Machado | December 31, 2010 at 3:32 am | Permalink

    When you consider "Many grids require two requests in order to display the first page of data" as a "design issue", aren’t you doing some kind of excessive, premature optimization?
    The "best practices" are telling you not to put a thousand of different components in your page - style sheets, images, flash files, etc - each one of them requiring one http request to load, not to avoid any visual component requiring more than one request, isn’t it?

  2. Igor | July 12, 2011 at 4:16 am | Permalink

    Craig,
    have you published any code in the end?
    I can’t find the link?

  3. Jesse Gavin | August 23, 2011 at 1:59 pm | Permalink

    I am extremely interested in the ideas you’ve presented here. Let me know if you’ve posted this code anywhere online and/or if I could contribute on any way.

{ 1 } Trackback

  1. A Better View API for Grids in ASP.NET MVC | December 30, 2010 at 2:19 pm | Permalink

    [...] post, I discussed some of the problems with existing grid components for ASP.NET MVC. Actually,… [full post] Craig Stuntz Craig Stuntz’s Weblog .netc#webanygrid 0 0 0 [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

Bad Behavior has blocked 713 access attempts in the last 7 days.

Close