Creating a custom Ajax Helper
Unobtrusive Ajax
Unobtrusive Ajax is an approach in which the ajax behaviors are separated out from the HTML elements. ASP.NET MVC framework provides supports for ajax using the AjaxHelper and AjaxHelperExtensions classes. For example you can easily create a HTML form that submits the data through ajax using the BeginForm extension method. The below listing shows how to create an ajax form in a razor view.
@Ajax.BeginForm("AddMovie", new AjaxOptions { UpdateTargetId = "divMovies" })
Listing 1. Creating an ajax form in razor
In views we can access the AjaxHelper through the property Ajax. Prior to version 3, MVC framework doesn't supports unobtrusive ajax so when you try to create an ajax form using the above code you will see the following html rendered to the browser.
<form action="/Movie/AddMovie" id="form0" method="post" onclick="Sys.Mvc.AsyncForm.handleClick(this, new Sys.UI.DomEvent(event));" onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, updateTargetId: 'divMovies' });">
Listing 2. Obtrusive html generated by the ajax form
You can notice the event handlers are directly specified in the html, also the arguments are passed as an object to the onsubmit handler. Yeah, the html looks ugly! The unobtrusive ajax helps to clean away the mess by separating the ajax behaviors from the DOM. Using unobtrusive ajax we not only gain clean html but also we can gracefully handle the cases where the browsers doesn't supports javascript. ASP.NET MVC 3 supports unobtrusive ajax with the help of jquery.
When unobtrusive ajax enabled this is what the form looks like,
<form action="/Movie/AddMovie" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#divMovies" id="form0" method="post">
Listing 3. Unobtrusive html generated by MVC 3
It's clearly evident the html is clean and easy to maintain.
How thing works?
The MVC framework represents the additional arguments that need to be passed for ajax calls as HTML5 data attributes in the form. Microsoft provides a library called jquery.unobtrusive-ajax.js that reads form elements those have the attribute data-ajax as true, extract the data attributes attached to the form and call the necessary jquery's ajax methods passing those arguments.
In MVC, to enable unobtrusive ajax we have to do couple of things.
1. Include both the jquery library and Microsoft's unobtrusive ajax library in views.
2. Set the UnobtrusiveJavaScriptEnabled as true in web.config.
Creating a custom ajax helper extension
The MVC 3 framework supports ajax behaviors for two html elements: forms and links. You can create an ajax form or an ajax link by the AjaxHelper class. To create an ajax form or an ajax link we need to supply the AjaxOptions instance to the extension method. The MVC framework writes the properties specified in the AjaxOptions instance as data attributes to the html elements.
Here is an example of creating an ajax link using one of the extension method provided by the AjaxHelper.
@Ajax.ActionLink("Load All Movies", new AjaxOptions { UpdateTargetId = "divMovies", InsertionMode = InsertionMode.Replace })
Listing 4. Ajax link
Like custom html helpers we can create custom ajax helpers. The approach is nearly same as creating a custom html helper, instead of creating extension method for the HtmlHelper class we have to do for AjaxHelper.
Let see how we can create an ajax search textbox by creating a custom ajax helper. Our search textbox make ajax calls to the server when we enter/change the text and dynamically update the html. First we have to create an extension method for AjaxHelper as shown in the below listing.
public static MvcHtmlString Textbox(this AjaxHelper ajaxHelper, string name, AjaxOptions ajaxOptions, object htmlAttributes) { var tag = new TagBuilder("input"); tag.MergeAttribute("name", name); tag.MergeAttribute("type", "text"); tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); tag.MergeAttributes((ajaxOptions ?? new AjaxOptions()).ToUnobtrusiveHtmlAttributes()); return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal)); }
Listing 5. Custom textbox ajax helper
Our extension method Textbox is quite simple takes three parameters: a name for the textbox, AjaxOptions instance and htmlattributes object. In the first three lines of the code we are creating the input element with appending the name and type attributes using the TagBuilder class. Then we are appending the additional html attributes passed as an anonymous object htmlattributes to the input element by calling the AnonymousObjectToHtmlAttributes method of HtmlHelper class.
What we are doing next is quite important, we are converting the passed AjaxOptions instance into HTML5 data attributes and appending to the input element by calling the AnonymousObjectToHtmlAttributes method of it.
So our search textbox is quite good but not ready to use. Unless we do some work in the client-side it simply doesn't work.
Extending the unobtrusive javascript library
The unobtrusive javascript library queries only for form and anchor elements. To enable ajax behaviors for textbox we have to modify the library.
$("input[type=text][data-ajax=true]").live("keyup", function (evt) { asyncRequest(this, { type: "GET", data: [{ name: $(this).attr(‘name'), value: $(this).val()}] }); });
Listing 6. Modifying unobtrusive ajax library for textbox helper
In the above script we query the textboxes that have ajax enabled and listen to the keyup event. Whenever the event fires we make an ajax call using the private helper method asyncRequest. We have to add this snippet right before or above the library queries for form and link elements.
Test drive
Finally we have to include the namespace where our textbox extension lives in the web.config under Views folder.
<system.web.webPages.razor> <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <pages pageBaseType="System.Web.Mvc.WebViewPage"> <namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="TextboxAjaxHelper.Extensions" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Routing" /> </namespaces> </pages> </system.web.webPages.razor>
Listing 7. Adding the extension namespace in web.config
The below listing shows how we can use our custom ajax helper in a view.
@Ajax.Textbox("search", new AjaxOptions { Url = "/Movie/Search", UpdateTargetId = "movies", InsertionMode = InsertionMode.Replace }, new { size = 50 })
Listing 8. Creating custom ajax textbox helper in razor view
Here is a sample application that describes how we can use our custom ajax helper.
Summary
In this article we saw about unobtrusive ajax and how we can create custom ajax helpers quite easily by adding extension methods to the AjaxHelper class.