Ajax with jQuery and ASP.NET MVC

Usually when doing Ajax with ASP.NET, you use some of Microsoft's own Ajax libraries on the server side to set things up for you. This makes it nice for point and click programming within Visual Studio, but as always with Microsoft solutions, doesn't make it very portable or maintainable. With one of my current projects, I wanted to use jQuery as the basis of my Ajax code, but still reap the benefits of ASP.NET MVC. To my delight, I found that the two work together really well.

The secret lies in using partial views. Parial views can be considered independent web controls that can be used within a regular view. Usually, these views are stitched together on the server side during the original request, but jQuery lets us load them dynamically. A parial view is perfect for features such as displaying a dynamic list, and parial views in ASP.NET MVC can be passed models just like regular views can. For a parial view that displays a list, I recommend passing it a data model that is a list. As a general rule of thumb, always start solving your MVC problems by developing the data model. Here's mine:

public class ItemList : List<ItemInfo>
{
    public int ObjectID { get; set; }
        public ItemList (int _ObjectID)
    {
        ObjectID = _ObjectID;
    }
}

In the above data model, ObjectID is an identifier of the object the list of items belongs to. Think of the main page as the view for the object, and the parial view as a listing of sub items. This model is the list of sub items that the parial view is going to work with.

The next thing we need is a partial view that uses this list of items:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyNamespace.ItemList>" %>
<script type="text/javascript">
    $(document).ready(function(){
        <% foreach(MyNamespace.ItemInfo item in Model) %>
        <% { %>
            $("#removeItem<%= Html.Encode(item.ID) %>").click(function() {
                $("#objectitems").load("/Object/RemoveItem", {
                    objectid: $("#ObjectID").val(), itemid: $("#ItemID<%= Html.Encode(item.ID) %>").val()});
                $("#newItem").load("/Object/NewItem", { objectid: $("#ObjectID").val() });
            });
        <% } %>
    });
</script>
<h3>Items:</h3>
<% if(Model.Count > 0) %>
<% { %>
    <%= Html.Hidden("ObjectID", Model.ObjectID) %>
    <ul>
        <% foreach(MyNamespace.ItemInfo item in Model) %>
        <% { %>
            <li>
                <%= Html.Encode(item.Name) %>
                (<a id="removeItem<%= Html.Encode(item.ID) %>" href="javascript:void(0)">remove item</a>)
                <%= Html.Hidden("ItemID" + Html.Encode(item.ID), item.ID) %>
            </li>
        <% } %>
    </ul>
<% } else {%>
    <i>no items defined yet</i>
<% } %>

The above code has two important sections. At the top, we see a script that uses jQuery to respond to click events. I'll come back to this later. The second section is where we display the contents of our list, and use links to "javascript:void(0)" in order to handle click events with the script. Each item has its ID appended to the end of the hyperlink's id attribute. We also write out hidden fields that we can then reference from the javascript.

Now look back up the the javascript section. Notice the use of ASP to generate as many click event handlers as there are items in the list. In this case, also, item IDs are appended to the id attributes of the hyperlink whose click we are handling. This javascript generation happens server side, so that there are enough event handlers on the client side. When the "remove item" link is clicked, we use jQuery to reload the "objectitems" div tag. This is a tag in which this whole partial view is wrapped inside the main view:

<div id="objectitems">
    <% Html.RenderPartial("ObjectItems", Model.Items); %>
</div>

By doing this, we can make the controller function for /Object/RemoveItem return the same partial view, thus replacing the partial view it was called from with an updated version:

[AcceptVerbs(HttpVerbs.Post)]
public PartialViewResult RemoveItem(int objectid, int itemid)
{
    if (this.ModelState.IsValid)
    {
        try
        {
            db.RemoveItem(itemid);
            db.Save();
            
            ItemList items = db.GetItems(objectid).ToList();
            
            return PartialView("ObjectItems", items);
        }
        catch (Exception)
        {
            //TODO: log/notify error
            ModelState.AddModelError("error", "An unexpected error occured, please contact the webmaster.");
        }
    }
    
    return PartialView("Error");
}

At the very end of the script's click event handler, we see a jQuery call to load Object/NewItem, which returns another partial view for creating an item. For simplicity, I won't cover this here, but you can see how partial views can be very handy when using Ajax.

This may not look like Ajax if you are used to calling jQuery's ajax() function. We are not receiving JSON data from the server, and then populating controls with that data. Instead, we are making use of jQuery's load() function and having the server populate our controls for us with ASP.NET MVC's models and views. In my opinion, this is usually preferable to simply handling everything on the client side, and can even be simpler to write by leveraging ASP.NET MVC for what it was made for.

Remember, that this is merely one solution to using Ajax with ASP.NET MVC. There are more traditional options, that may or may include the use of jQuery. This is simply the solution that I have found works best for me. I feel that by using jQuery, I could more easily port my application from ASP.NET to another language. However, at the same time I am not missing out on any of the features that the current language has to offer.