Introduction
In ASP.NET MVC 1.0, we shipped Ajax helpers implemented as extension methods on the AjaxHelper class (and available via the Ajax property of your views). In the box, we shipped two category of Ajax helpers: Ajax links and Ajax forms. Both fundamentally did the same thing: make an asynchronous request, and do something with the result when you’re finished (including knowing whether you got back a success or failure response from the server).
In ASP.NET MVC 3 Beta, we've updated the runtime to enable a feature we're calling "Unobtrusive Ajax". We have also created a consumer for these unobtrusive Ajax attributes that uses jQuery to perform the Ajax requests on our behalf.
Disclaimer
This blog post talks about ASP.NET MVC 3 Beta, which is a pre-release version. Specific technical details may change before the final release of MVC 3. This release is designed to elicit feedback on features with enough time to make meaningful changes before MVC 3 ships, so please comment on this blog post or contact me if you have comments.
Telling Us What To Do: AjaxOptions
The AjaxHelper extension methods – ActionLink and RouteLink for Ajax links, and BeginForm and BeginRouteForm for Ajax forms – have APIs which are very similar to their HtmlHelper counterparts. The primary difference is that the AjaxHelper versions all take an additional parameter in the form of the AjaxOptions class:
public class AjaxOptions { public string Confirm { get; set; } public string HttpMethod { get; set; } public InsertionMode InsertionMode { get; set; } public int LoadingElementDuration { get; set; } public string LoadingElementId { get; set; } public string OnBegin { get; set; } public string OnComplete { get; set; } public string OnFailure { get; set; } public string OnSuccess { get; set; } public string UpdateTargetId { get; set; } public string Url { get; set; } }
The properties here are all used to tell MVC exactly what you want your Ajax call to do.
Rendering: Traditional
When unobtrusive mode is off, MVC will render inline JavaScript on your <a> and <form> elements that’s dependent on the ASP.NET Ajax library (MicrosoftAjax.js) and the MVC Ajax library that uses ASP.NET Ajax (MicrosoftMvcAjax.js):
<form action="/ajax/callback" 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, loadingElementId: 'loading', updateTargetId: 'updateme' });">
This behavior is identical to the behavior in MVC 1.0 and MVC 2.
Rendering: Unobtrusive
When unobtrusive Ajax mode is enabled in MVC, the HTML that we generate looks significantly different:
<form action="/ajax/callback" data-ajax="true" data-ajax-loading="#loading" data-ajax-mode="replace" data-ajax-update="#updateme" method="post">
Probably the first thing you’ll notice is that the HTML is very readable (and thus, very tweak-able when doing debugging). Using simple names and values increases readability. Another thing that’s obvious is that the rendered size of the HTML content is quite a bit smaller in unobtrusive mode, even with semi-verbose attribute names. The HTML is HTML 5-compatible, using the extensible attribute (data-) syntax. We’re also using CSS selectors for increased flexibility on the client-side for things choosing the loading and update elements.
Enabling Unobtrusive Ajax
In MVC 3, we have a single flag to turn on unobtrusive JavaScript mode, which enables both unobtrusive Ajax and unobtrusive client validation (which we’ll talk about in the next blog post). Unobtrusive JavaScript mode is turned off by default for backward compatibility with projects upgraded from MVC 1.0 and MVC 2. However, we have turned it on in the MVC 3 project template, so new projects will begin using the unobtrusive JavaScript support by default.
To turn unobtrusive JavaScript mode on/off by default for the entire application, you can use Web.config:
<configuration> <appSettings> <add key="UnobtrusiveJavaScriptEnabled" value="true"/> </appSettings> </configuration>
You can also turn it on or off with code:
HtmlHelper.UnobtrusiveJavaScriptEnabled = true;
Using code to turn it on or off actually behaves contextually. If that line of code is present in your Global.asax file, then it turns unobtrusive JavaScript on or off for the whole application. If it appears within your controller or view, on the other hand, it will turn it on or off for the current action only.
In addition to setting the flag, you will also need to include two script files: jQuery (~/Scripts/jquery-1.4.1.js) and the MVC plugin for unobtrusive Ajax with jQuery (~/Scripts/jquery.unobtrusive-ajax.js).
An interesting note: since there is no actual JavaScript being emitted when you use unobtrusive Ajax, if you forget to include one or the other script, you won’t see any errors when attempting to submit the Ajax request; it will simply behave like a non-Ajax request.
Mapping AjaxOptions to attributes
The following table lists the mapping from AjaxOptions members to the HTML 5 data attributes:
AjaxOptions | HTML attribute |
---|---|
Confirm | data-ajax-confirm |
HttpMethod | data-ajax-method |
InsertionMode | data-ajax-mode * |
LoadingElementDuration | data-ajax-loading-duration ** |
LoadingElementId | data-ajax-loading |
OnBegin | data-ajax-begin |
OnComplete | data-ajax-complete |
OnFailure | data-ajax-failure |
OnSuccess | data-ajax-success |
UpdateTargetId | data-ajax-update |
Url | data-ajax-url |
In addition to these attributes, there will always be a data-ajax="true" attribute present to trigger the unobtrusive Ajax system.
* = data-ajax-mode will only be present if UpdateTargetId is set.
** = data-ajax-loading-duration will only be present if LoadingElementId is set.
The Ajax Callbacks
One major aspect that will need to change when you convert from traditional to unobtrusive JavaScript is the code inside your Ajax callbacks. Since the callbacks are specific to the actual Ajax library being used, any code you have in MVC 1.0/2 applications will by definition be code that’s expecting ASP.NET Ajax style callbacks.
When you’re using MVC 3’s unobtrusive Ajax support with jQuery, your callbacks will be based on the four callbacks from jQuery.Ajax; specifically, OnBegin maps to “beforeSend”, OnComplete maps to “complete”, OnFailure maps to “error”, and OnSuccess maps to “success”. When providing values for the Ajax callbacks, you can provide one of two strings: either the name of a function that’s defined in JavaScript, or inline code.
If your handlers are defined as function names, then the handler functions should have signatures that match those in the jQuery.Ajax documentation. The values from jQuery will be passed directly to the handler function.
If your handlers are defined as inline code, then they have implicit access to the parameters of their respective jQuery.Ajax handler functions. All handlers have access to the XMLHttpRequest object via a parameter named “xhr”. Inline code for all handlers except OnBegin will have access to a “status” parameter. Finally, inline code for OnError will have access to an “error” parameter, and inline code for OnSuccess will have access to a “data” parameter.