This article is about pitfalls which should be considered when you’re implementing an Ajax grid with jQuery and ASP.NET MVC. The sample in the article can be used as a starting point to more complicated version of any grid since it provides a complete set of required features: create, edit, delete, filtering, sorting and pagination. The article also explains the conceptual design of the grid and shows how client and server code should interact to each other.
This article assumes you’re friendly with ASP.NET MVC and jQuery, but do not have everyday experience with them.
The sample application you can find here: http://ajaxgrid.apphb.com/
The source code can be downloaded here: http://github.com/xtrmstep/MvcAjaxGridSample
Conceptual Design
There are several important points which you shall consider up-front when starting an Ajax grid design. These points are data interchange, validation and security. Two last points will be explained further in the text. Now let’s stop and take a closer look at the data interchange.
Any grid has a state and data which we see. The state represents grid’s filtering, sorting and pagination. Data which we see in the grid is not a part of its state. My sample has a stateless server side implementation and that’s why all information about current grid’s state is stored on the client side, in browser. In order to support it you can use JSON but attributes of HTML elements can be also used with the same efficiency. Since all information is stored in the browser each request to the server should contain an original state and changes to it. This is because some state conditions might be invalid and should be reset to the original one. The original one is that which were loaded with the page during previous request.
Operations upon data have a bit more complicated scenario. Some of them affect grid’s state, some of them do not. For example, when you’re deleting an item the grid’s pagination or filter state can be changed. Another point is that some operations need more interactions with a user than others. For example, create-edit operations need extra inputs to change fields and should provide input validation.
The create-edit operations are noticeable not only because of extra inputs, but also due to ways how these inputs are provided. In more advanced scenarios you want use inline inputs or forms implemented in JavaScript. Such generic solutions are out of the scope of this article. My sample shows the approach which is more suitable for .NET applications. The form with inputs elements is rendered on the server-side once the operation is requested. This is because the sample uses in-built validation with data annotation attributes.
Common Pitfalls
The following pitfalls were solved in the sample project which you can download and run on your PC. You also may try it online.
Pitfall #1: Grid based on MVC Ajax Helper only requires data duplication in the client code
Most of scenarios need the web form posting. It’s inefficient to post the grid with all data to the server to send information about filtering, sorting and pagination. You should not implement the grid using only the helper. You will end up that you need duplicate grid’s state in every row of the grid. Moreover some of the scenarios become too complex technically. Just forget and move on.
Pitfall #2: All requests should contain information about grid state
As stated above the grid’s state describes filtering, sorting and pagination. Since the server-side code does not know about the state, nevertheless it still shall produce views which contain it. Only create and edit requests can omit that extra data because it will be added to the save operation later.
Pitfall #3: HtmlHelper uses ModelState buffer to retrieve values on postback operations
You may notice that on postback operations values stored in hidden inputs stay unchanged. It is so even if you assign a new value on the server-side before responding. This is known, but not broadly described bug (or feature) in the implementation of Html.Hidden(). I found other mentions of the same issue and it looks like affect all helper methods. In order to overcome that you shall make your own helper. See this for another case: http://weblog.west-wind.com/posts/2012/Apr/20/ASPNET-MVC-Postbacks-and-HtmlHelper-Controls-ignoring-Model-Changes
Pitfall #4: MVC DataAnnotation validation result is not returned to the client after Ajax POST
MVC does not have a standard way of sending validation errors back to client on POST requests. You should make your own approach to expose validation errors to client. In the sample the server returns error 500 if the model is not correct and provide a list of fields with error messages. The client code highlights inputs according to that information in its turn.
Pitfall #5: Error messages can be not enough when IValidatableObject is used
Be careful if you override IValidatableObject in your model to do manual validation and mix it up with DataAnnotation attributes. If you go this way your error messages would not enough. For example, you have some simple validations, string length, using attributes, and more complex one via the interface (some relations between fields). The validation engine goes through attributes first and return if found any error. Thus, you’ll get only one error in UI even if the complex validation also cannot be proceeded. Try to not mix up attributes and validatable object.
Pitfall #6: MVC model binding is not compliant with JSON notation
If you send a complex JSON object to the server your model will be initialized without nested objects. In order to overcome that you want use a function which converts JSON data to be MVC model binding compliant. The good jQuery plugin which implements that is $.postfix() which I found here http://www.nickriggs.com/posts/post-complex-javascript-objects-to-asp-net-mvc-controllers
Pitfall #7: Use AntiForgery token for all POST requests
MVC does not provide a way to add the token to all POST requests. In order to overcome that you should manually add it to each request as described in my another article.
Pitfall #8: Property names in ValidationResult might be renamed by MVC engine
(This is not solved pitfall.)
Let’s assume you override IValidatableObject interface and use property names in ValidationResult objects. For instance, in case of property “Title” you would write something like this:
result.Add(new ValidationResult("Value is required.", new[] {"Title"}));
Then if you send the result back to the client it renames fields according to pattern “<parameterName>.<propertyName>”. Parameter name is the name of action’s parameters (see Save() in the sample). But it’s a tricky case since if you had not sent any value to the parameters before that, the field would not be renamed. In both cases the model is invalid. In order to overcome the issue the sample uses a hack in JS code to remove the prefix. I have not found a way to solve that in general.