Skip navigation

Posting Forms the AJAX Way in ASP.NET MVC

Create AJAX-enabled HTML Forms in ASP.NET MVC Pages

RELATED: "ASP.NET MVC: Ready for the Enterprise" and "AJAX Features in ASP.NET MVC."

There are only two ways in which a web page can place an HTTP request to a server-side URL. You can set the target URL as the action attribute of an HTML form or you can instruct the browser s XMLHttpRequest object to reach the URL using an HTTP request. The latter scenario represents the typical AJAX scenario, where the client page gains control over the entire operation. In that case, a piece of JavaScript code does the trick of invoking the URL, passes some input data, and gets any response. Implementing this scenario in ASP.NET MVC only requires you to get familiar with and use some ad hoc tools, like the AJAX methods in the jQuery library, or in any other advanced JavaScript library with which you feel comfortable.

What if, instead, you re a server-side person and don t like JavaScript that much? ASP.NET MVC provides an alternative API to add AJAX capabilities to pages, while still remaining in the realm of markup and server code.

In this article, I ll examine an AJAX-related feature of the ASP.NET MVC Framework that basically implements a form of partial rendering on top of the new ASP.NET MVC programming model. In a nutshell, I ll discuss how to post the content of an HTML form to a server controller and update the current view without incurring a full page refresh.

The HTML Message Pattern

Before I go any further, let me briefly recall the underlying pattern we are silently applying here. As thoroughly described at www.ajaxpatterns.org, the HTML Message pattern refers to a situation in which a web page invokes a remote URL and receives a plain HTML response. The URL, whether be it a web/WCF service or a plain REST service, doesn t return data only, but rather a UI-ready string that the caller will take and render out.

HTML Message is the pattern living behind the partial rendering approach in classic ASP.NET, and also is the pattern that some commercial libraries of controls implement to give you AJAX-enabled server controls. Two libraries that do so are Telerik RadControls and Gaiaware.

AJAX can be done in either of two ways: bring plain data to the client and arrange the UI or bring pre-arranged HTML on the client. Partial rendering clearly addresses the second option, and so it is when you use AJAX facilities in the ASP.NET MVC Framework.

The AJAX BeginForm Helper

AJAX support for ASP.NET MVC views is built in to the Ajax helper class. The class features the members listed in the table in Figure 1.

Member

Description

ActionLink

Generates an anchor tag whose action URL is based on the specified parameters.

BeginForm

Generates a

tag with an action URL that performs an AJAX operation.

RouteLink

Generates an anchor tag whose action URL is based on the specified route name.

ViewContext

An object that contains information about the view data, controller, and temporary data.

Figure 1: Members of the Ajax helper class

In particular, the BeginForm helper method generates an AJAX-enabled form tag. Here s how to use it:

<% using (Ajax.BeginForm("GetCustomerDetails",

new AjaxOptions { Confirm = "Are you sure?",

LoadingElementId = "lblWait",

UpdateTargetId = "pnlDetails" }))

{ %>





<% } %>

The first argument to BeginForm indicates the action you want to execute once the form is posted. The name of the target controller can be explicitly mentioned using one of the numerous overloads of BeginForm. If not specified, it is inferred from the content of ViewContext.

<% = Ajax.ViewContext.Controller.ToString() %>

If not explicitly specified, the default controller is the controller that ordered the rendering of the current view. A second fundamental parameter for the BeginForm method is an instance of the AjaxOptions class. The members of the AjaxOptions class are detailed in the table in Figure 2. Essentially, the AjaxOptions class lets you specify information that will help the framework carry the operation the way you want. At the very minimum, you might want to indicate the ID of the element that will receive any HTML message that the controller method may have generated on the server. The ID is set through the UpdateTargetId member of the AjaxOptions class.

HttpMethod

String property, indicates the HTTP operation to be performed. The property value is set to POST by default.

InsertionMode

Indicates how any HTML response should be inserted in the current page DOM. Feasible values for the property come from the InsertionMode enumerated type: Replace, InsertBefore, InsertAfter. The element to replace is the element pointed by the UpdateTargetId property. The default value is Replace.

LoadingElementId

String property, gets and sets the ID of the DOM element to be displayed for the time it takes to complete the request.

OnBegin

String property, gets and sets the name of an optional JavaScript function to be executed just before submitting the request.

OnComplete

String property, gets and sets the name of an optional JavaScript function to be executed once the request has completed.

OnFailure

String property, gets and sets the name of an optional JavaScript function to be executed in case of a failed request.

OnSuccess

String property, gets and sets the name of an optional JavaScript function to be executed if the request completes successfully.

UpdateTargetId

String property, gets and sets the ID of the DOM element to be updated with any HTML response coming back from the server.

Url

String property, gets and sets the actual URL the request should be sent to. If specified, the property takes priority over the action attribute of the tag.

Figure 2: Members of the AjaxOptions class

I also declared the LoadingElementId property in the preceding code snippet. This property indicates the ID of the user interface element you want to display temporarily as the request goes. The role of the LoadingElementId property has some analogy with the UpdateProgress control you may recall from classic ASP.NET partial rendering. Given the preceding code snippet that uses Ajax.BeginForm, the corresponding resulting markup you ll find in the browser is shown in Figure 3.







Figure 3: The resulting markup when using Ajax.BeginForm

As you can see, the onsubmit attribute points to a framework-provided object the Sys.Mvc.AsyncForm object. The object is defined in the MicrosoftMvcAjax.js file that is referenced automatically from any AJAX-enabled ASP.NET MVC page. Figure 4 shows the source code of the class.

Sys.Mvc.AsyncForm = function Sys_Mvc_AsyncForm()

{

}

Sys.Mvc.AsyncForm.handleSubmit = function

Sys_Mvc_AsyncForm$handleSubmit(form, evt, ajaxOptions)

{

/// 

/// 

/// 

/// 

/// 

/// 

evt.preventDefault();

var body = Sys.Mvc.MvcHelpers._serializeForm(form);

Sys.Mvc.MvcHelpers._asyncRequest(form.action,

form.method || 'post', body, form, ajaxOptions);

}

Figure 4: The AsyncForm class

Without going into the nitty-gritty details of the internal members, the overall behavior of the object comes out quite clearly. The handleSubmit method first prevents the default browser event from taking place (so no browser post will ever occur). Next, it serializes to a string the current content of the

tag and sends out a request using the infrastructure provided by the Microsoft AJAX library. (At the time of this writing there was no RTM version ready, but it s possible that by the time you read this you ll be able to plug in your favorite AJAX library and drop Microsoft s.)

Inside the Request Cycle

As shown in Figure 4, the submission of the request begins when the _asyncRequest helper method is invoked. The first thing this method does is show you a confirmation dialog box (see Figure 5). If the user confirms, the method proceeds and determines the target URL. The target URL isn t necessarily the form action URL being passed to the method on the command line. If the AjaxOptions object contains a non-empty Url property, this URL is used instead. Next, the OnBegin JavaScript callback is invoked, if such a callback was specified. By using this callback, you can further modify the URL programmatically, as I ll show in a moment.

Figure 5

Figure 5: The AJAX request is about to start

The request is then sent asynchronously; when any response comes back, the OnComplete JavaScript callback is fired. Note that this callback is invoked before examining the status code of the response. If the request completed successfully, the DOM is updated and the OnSuccess callback is run. If not, no updates are made and the OnFailure callback is executed.

The progress indicator, or whatever piece of user interface constitutes the progress template, is displayed right after the invocation of the OnBegin callback and programmatically hidden right after the OnSuccess callback has been called.

Posted Data and Response

The handleSubmit JavaScript method that fires when the user clicks the submit button of the form serializes to a string the content of the form elements. The string will then have the canonical shape of an HTTP POST body, as in the code snippet shown here:

TextBox1=Dino&TextBox2=Esposito&CheckBox1=on

On the server, the target URL can retrieve these values using the ASP.NET Request.Form collection and use posted data to perform any required server action. Figure 6 shows the details of the request body for the page in Figure 5. In this case, the form includes the sole listbox whose ID is ddCustomerList. The second element you see in Figure 6 refers to an internal flag that denotes an ASP.NET MVC asynchronous call.

Figure 6

Figure 6: Sample of data posted in an ASP.NET MVC AJAX request

Earlier in the article I repeatedly mentioned the HTML Message pattern as the pattern according to which the target URL replies to a request by sending out plain HTML. In this case, the target URL is a method on a given controller class. This method is expected to return a string and have a signature like this:

public string GetCustomerDetails(string ddCustomerList)

Note that if the parameter name matches an input element in the posted data, then ASP.NET MVC can resolve the mapping automatically. In this case, the posted value of the element named ddCustomerList is assigned to the corresponding formal parameter. If not, you can give the method an empty signature and resort to the following code:

string id = Request.Form["ddCustomerList"].ToString();

The controller s method does its own work and produces data for the response. If you design the method to return a string, you must serialize data to an HTML string, as shown here:

public string GetCustomerDetails(string ddCustomerList)

{

string id = ddCustomerList;

Customer cust = GetCustomerInSomeWay(id);

return FormatAsHtml(cust);

}

It s nice to notice that you also can return an object not an HTML string from the method. In this case, the client page will still receive a string; in particular, it will be the string generated by the ToString method on the object. In other words, the following code produces an equivalent result and might be more elegant to use in some cases:

public Customer GetCustomerDetails(string ddCustomerList)

{

string id = ddCustomerList;

Customer cust = GetCustomerInSomeWay(id);

return cust;

}

:

public partial class Customer

{

public override string ToString()

{

return FormatAsHtml(this);

}

}

I m obviously assuming that Customer is a partial class like those auto-generated by the LINQ to SQL wizard.

Adapting the URL Dynamically

To top off this article, let me briefly discuss a scenario that, although not particularly common, may sometimes show up. Let s suppose you don t want to post data through a form. (In ASP.NET MVC, you are no longer limited to one form per page.) You have a link button and want to invoke a URL to get some HTML. In this case, you have two options. The first entails that you explicitly write a click handler for the link button and adjust the call at your convenience. The second option is using the ActionLink helper, which requires less code and benefits from services offered by the platform:

<%= Ajax.ActionLink("Details", "/GetCustomerDetails",

new { id = "xxxxx" },

new AjaxOptions { HttpMethod="GET",

LoadingElementId="lblWait",

UpdateTargetId="pnlDetails",

OnBegin="adjustURL" })%>.

The code generated by ActionLink provides for preparing the request and updates the user interface. However, it doesn t provide an automatic mechanism for binding input data to the URL. Suppose you want to add to the URL the ID of the customer currently selected in a list. This is a no-brainer if you re writing a click handler yourself, but does pose a challenge if you intend to use the helper ActionLink.

A possible workaround consists of placing a placeholder in the URL (such as xxxxx in the snippet above) and replacing that in the OnBegin callback:

function adjustURL(context)

{

var list = $("#ddCustomerList")[0];

var id = list.options[list.selectedIndex].value;

var request = context.get_request();

var url = request.get_url();

url = url.replace(/xxxxx/, id);

request.set_url(url);

}

Conclusion

AJAX in ASP.NET MVC is definitely possible, and various tools have been provided for it. In addition to using plain JavaScript code for setting up a request and updating the user interface, you can rely on a couple of powerful helpers such as ActionLink and, more importantly, BeginForm, through which you can get the ease of use of partial rendering without the burden of the viewstate.

Dino Esposito ([email protected]) is an architect at IDesign, specializing in ASP.NET, AJAX, and RIA solutions. Dino co-authored Microsoft .NET: Architecting Applications for the Enterprise and Microsoft ASP.NET and AJAX: Architecting Web Applications.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish