Html Forms, Ajax, ASP.NET MVC and You

Dino Esposito | June 24, 2010

 

Over the Web, most of the time clients interact with the Web server in two ways: posting the content of a HTML form and placing a GET request for the content pointed by a given URL. Both types of requests are usually handled by the Web browser and resolved with a full page refresh. The advent of Ajax didn’t change this basic fact, but changed our perception of the expected result. In a nutshell, users and developers no longer want full page refreshes to see displayed the results of a server operation.

As emphatic as it may sound, by saying so we are simply questioning the pillars of the Web as we know it today. Finding a widely accepted alternate approach to posting data that bypasses the browser would be the same as defining a new paradigm for Web programming. This would probably be the final step in that long process—aptly referred to as paradigm shift—that many agree started with the rediscovering of the XmlHttpRequest object a few years ago.

Today, we have two basic approaches to setting up client/web server communication. One is a pure client/server approach in which the client is a piece of JavaScript code running within the browser and the server is a layer of services specifically created to serve Ajax requests.  The other approach is something that falls in between this JavaScript intensive pattern and the classic server-side Web programming pattern. The second approach consists in having a regular HTML form that posts, but the form submission is hooked up by some external script that swallows the original request and replaces it with its own asynchronous request.

What’s the difference? And what does each choice mean to you as a developer? Let’s find out.

One Ajax Paradigm; Two General Patterns

The approaches are summarized by two fundamental Ajax patterns: the Browser-side Template pattern (BST) and the HTML Message pattern (HM). You can find more information on these and other more specific patterns on https://ajaxpatterns.org

The BST pattern is about sending asynchronous requests to the server to get raw data on the client. The user triggers a remote call by interacting with the user interface of the current page. The action downloads some data to the client. It is important to notice that the pattern recommends that only data is sent over the wire—no HTML and layout information at all.

Any received data is then managed by a JavaScript callback. The JavaScript callback function is in charge of instantiating a new breed of component—the markup builder. The markup builder receives a reference to one or more HTML templates in the page DOM. It also gets the downloaded data, does its job and returns a HTML string. Finally, the callback injects the string in the current document object model (DOM) of the page.

The HM pattern instead assumes that the server endpoint returns a response that is ready to be incorporated in the current DOM. The response, in other words, doesn’t need any client side processing except whatever it takes to the client code to display the received markup. In a HM scenario, the endpoint will return a HTML message; not an entire page, but a significant chunk of markup to replaced or inserted somewhere in the existing page DOM.

Today, frameworks for web development are being built around one of these patterns or to support both in some ways. We find today the BST approach behind popular libraries such as jQuery. You need a library like jQuery (and many of its plug-ins) if you want to be able to post a form, get some fresh data and then run any client logic you need to update the user interface accordingly. In doing so, you are moving part of your presentation layer to the Web browser and splitting it between a client-only, JavaScript-based tier and a server-side, ASP.NET hosted tier that contains the controller logic.

If you’re coming from an ASP.NET experience, this approach is significantly new and may be hard to push throughout a company. On the other hand, this approach is really effective if managed by capable hands.

The HM pattern is closer to classic ASP.NET development and represents the evolution of the ASP.NET programming model to incorporate the Ajax paradigm. We find an implementation of the HM pattern in the partial rendering API of ASP.NET Web Forms. We also find an implementation of the pattern in the Ajax API offered by ASP.NET MVC.

Let’s see how to add Ajax capabilities to an ASP.NET MVC application.

Defining an Ajax Form in ASP.NET MVC

If you decide to add Ajax capabilities to an ASP.NET application, then the first feature you want is being able to post from a form and just update (opposed to refresh) the current page. For this to happen, your code needs to hijack the control of the form submission process. This can be done only via scripting. But thankfully you are not called to write this special piece of script code yourself over and over again. The ASP.NET MVC framework has some facilities you bring in using a more familiar model.

In ASP.NET MVC, a classic HTML form is defined as follows:

<% Html.BeginForm(actionName, controllerName) { %>

    <input type="text" id="Name" ... />
    <input type="text" id="Date" ... />
    <hr />
    <input type="submit" value="Save" />

<% } %>

The surrounding code block just outputs the <form> opening and closing tags with a programmatically generated URL that binds to the specified controller and action. As an ASP.NET MVC developer, this code is nothing new and sounds pretty familiar. Here’s what it takes to make it Ajax-enabled.

<% Ajax.BeginForm(actionName, controllerName, ...); %>

    <input type="text" id="Name" ... />
    <input type="text" id="Date" ... />
    <hr />
    <input type="submit" value="Save" />

<% Ajax.EndForm(); %>

The biggest change is switching to a different underlying object that generates any necessary markup. The Ajax object will generate a piece of HTML like below. Let’s assume that Home is the controller name and GetCustomerDetails is the action name:

<form action="/Home/GetCustomerDetails" 
      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: 'lblWait', 
                          updateTargetId: 'pnlDetails' });">

    <input type="text" id="Name" ... />
    <input type="text" id="Date" ... />
    <input type="submit" value="Save" />

</form>

A JavaScript function—handleSubmit—hooks up the form submission. Its first step consists in preventing the default event handler from triggering. In this way, the classic browser-led form submission process is stopped and replaced with a custom one that works asynchronously. The same handleSubmit function then proceeds with a classic Ajax requests that mimics the typical behavior of a form submission. This function is defined in the MicrosoftMvcAjax.js file that Visual Studio 2010 gently adds automatically to any new ASP.NET MVC project you create.

The Ajax-led request hits the ASP.NET server application and is resolved by executing the specified action on the given controller. What about the response? An action method designed to be invoked from an Ajax request can’t return a ViewResult object. The following method, for example, won’t work.

public ActionResult GetCustomerDetails( ... )
{
   :
   return View();
}

The code won’t throw any exception, but it will return an entire HTML page that the client script will try to insert at a specific location in the current DOM. The final result will likely be significantly different from your expectations. So in first place, a controller method invoked via Ajax must return a partial view, that is some form of content generated from a user control.

[AjaxOnly, HttpPost]
public ActionResult GetCustomerDetails( ... )
{
   :
   return PartialView();
}

To avoid further troubles, you might also want to decorate the method with a handmade AjaxOnly attribute that would block any attempt to call the method from a place where a full page is expected as a response. The AjaxOnly attribute is not part of the ASP.NET MVC framework, but can be easily coded as shown below:

public class AjaxOnlyAttribute : ActionMethodSelectorAttribute 
{
    public override Boolean IsValidForRequest(
             ControllerContext controllerContext, MethodInfo methodInfo)
    {
        return controllerContext.HttpContext.Request.IsAjaxRequest();
    }
}

Updating the User Interface

If you look back at the markup you employ to define an Ajax form, you find out that we left a few parts of it unspecified. Here’s what I presented a moment ago:

<% Ajax.BeginForm(actionName, controllerName, ...); %>

The two parameters—actionName and controllerName—serve the purpose of defining the action URL. However, some other information is missing. In particular, which part of the current page should be updated to reflect the results of the server operation? That piece of information must be added to the form to be used by the script code that handles the form submission.

In ASP.NET MVC, you use the AjaxOptions class to specify information and any additional behavior to be kept during the execution of the request. Table 1 lists the members of the AjaxOptions class.

Property Description
Confirm Indicates the JavaScript function to call to have a confirmation before the request executes.
HttpMethod Indicates the HTTP method to use for the request.
InsertionMode Indicates the insertion mode for any content downloaded that has to be injected in the current DOM. Feasible values are Replace, InsertBefore and InsertAfter.
LoadingElementId Indicates the ID of the DOM element to be displayed while the request in ongoing. The element’s visibility is managed automatically and the element is shown when the request begins and is hidden upon completion.
OnBegin Indicates the JavaScript function to call before the request executes. If the function returns false, the request is canceled.
OnComplete Indicates the JavaScript function to call when the request has completed but before failure or success is determined.
OnFailure Indicates the JavaScript function to call when the request completes with a failure. Failure is detected through the status code of the response packet.
OnSuccess Indicates the JavaScript function to call when the request completes successfully.
UpdateTargetId Indicates the ID of the DOM element to be updated with any HTML content downloaded.
Url Indicates the target URL of the request if not already specified in the markup as when a link or a form is used.

Table 1—Members of the AjaxOptions class

At the very minimum, you need to specify a value for the UpdateTargetId member. The member indicates the portion of the DOM where the content of the received HTML message will be inserted or replaced.

This aspect marks a key difference with the non-Ajax scenario. An Ajax-enabled form must indicate where any received response has to be placed. The ASP.NET MVC API requires that the method you post to returns a chunk of HTML. This rather automated behavior makes it so easy to add Ajax capabilities to a HTML form.

Based on personal experience, I have to say that when I first studied the Ajax API in ASP.NET MVC I wasn’t quite impressed and reckoned it to be not so seamless to use in the entire scope of an application. But when I used it in a real application it was a blast. It proved smooth and effective. It preserves the pleasure of writing methods (instead of postback handlers) to serve a request and keep the markup reusable and flexible to some extent by forcing you to define a response in a user control.

The OnXxx members you see in Table 1 offer another level of control over the entire Ajax process. By writing JavaScript code you can hook up three stages of the process—when it begins, when it has completed, and the result (failure or success). In the OnBegin handler, you can trigger some client-side validation and return false if it fails.

// Example of OnBegin handler
function beginOperation() {
   if (!validateInput())
      return false;
   return true;
}

Note that due to a small bug in MicrosoftMvcAjax.js when OnBegin returns false the operation is correctly aborted, but the progress interface—if you specified it via LoadingElementId—is still displayed and will stay on forever. There’s no workaround other then editing the MicrosoftMvcAjax.js file. Here’s the point of intervention:

Sys.Mvc.MvcHelpers._asyncRequest = function ... {
   :
   if (ajaxOptions.onBegin) {
        continueRequest = ajaxOptions.onBegin(ajaxContext) !== false;
    }

    // You need this IF here 
    if (!continueRequest)
        return;

    if (loadingElement) {
        Sys.UI.DomElement.setVisible(ajaxContext.get_loadingElement(), true);
    }
    if (continueRequest) {
        request.add_completed(Function.createDelegate(null, function(executor) {
            Sys.Mvc.MvcHelpers._onComplete(request, ajaxOptions, ajaxContext);
        }));
        request.invoke();
    }   
}

Once you fixed this, you might want to use the Ajax minifier tool to compress an otherwise large script file to a reasonable size. You can get the Ajax minifier tool from https://aspnet.codeplex.com/releases/view/40584.

In other cases, it can helpful to fix up the URL to make sure it reflects some selection the user may have made. OnComplete fires as soon as the operation has completed but before the results are processed. The event fires regardless of the next handler—be it OnSuccess or OnFailure.

Dealing with Notifications

A problem that you face with Ajax forms has to do with messages shown to the user in case of a failure or success. Suppose for example that your form will save a record to some database. If it fails, you want to show an appropriate error message; if it completes successful, you want to let the user know that all went just fine.

In a classic HTML scenario, the server will just serve two distinct pages. More importantly, the user lands on a message page and have to explicitly move away from there to go back to the previous stage. In Ajax, the controller method has to return some HTML which will be displayed in the current page. What happens next, however, is different for failure or success. In case of failure, you just display any error message and stay in the form. In case of success, you can display the message for a few seconds and then move away to the next stage. You can code these distinct pieces of logic in OnSuccess and OnFailure but for this to happen, though, it is key that the server method signals whether the operation completed successfully or not.

How can the method do this as it is simply expected to return a chunk of HTML? The answer is fairly obvious—using HTTP codes. The method will write out any HTML response but will set the status code to 500 if the HTML represents an error message. In my applications I usually employ a special action result class that extends the base PartialViewResult to set a specific error code if the partial view represents an error. Here’s some sample code:

public class PartialViewWithErrorResult : PartialViewResult
{
    public Int32 StatusCode { get; set; }

    public PartialViewWithErrorResult() : this(HttpStatusCode.InternalServerError)
    {
    }
    public PartialViewWithErrorResult(Int32 statusCode)
    {
       StatusCode = statusCode;
    }
    public PartialViewWithErrorResult(HttpStatusCode statusCode)
    {
       StatusCode = (Int32) statusCode;
    }

    public override void ExecuteResult(ControllerContext context)
    {
       if (context == null)
          throw new ArgumentNullException("context");

       if (string.IsNullOrEmpty(ViewName))
          ViewName = context.RouteData.GetRequiredString("action");

       ViewEngineResult result = null;
       if (View == null)
       {
           result = FindView(context);
           View = result.View;
       }

       // Set the status code
       context.HttpContext.Response.StatusCode = StatusCode;

       // Output
       var output = context.HttpContext.Response.Output;
       var viewContext = new ViewContext(context, View, ViewData, TempData, output);
       View.Render(viewContext, output);
       if (result != null)
          result.ViewEngine.ReleaseView(context, View);
    }
}

The PartialViewWithErrorResult  class goes hand in hand with an extension method to quickly create and instance.

public static PartialViewWithErrorResult PartialViewWithError(
     this Controller controller, String viewName, Object model, Int32 statusCode)
{
   if (model != null)
      controller.ViewData.Model = model;

    var result = new PartialViewWithErrorResult
    {
         StatusCode = statusCode,
         ViewName = viewName,
         ViewData = controller.ViewData,
         TempData = controller.TempData
    };
    return result;
}

A controller action method invoked over an Ajax request will have the following structure:

[AjaxOnly, HttpPost]
public ActionResult InsertBooking(BookingInputModel inputModel)
{
   var newBookingDto = new BookingDto {...};
   var response = _bookingService.InsertNewBooking(newBookingDto);
   if (!response.Success)
   {
      return this.PartialViewWithError(
              UserControls.Failed, 
              response, 
              (Int32) HttpStatusCode.InternalServerError);
   }

   // Success
   response.Success = true;
   response.ErrorMessage = AppResources.BookingSuccessful;
   return PartialView(UserControls.Inserted, response);
}

In this example, the physical operation is carried by an application service accepting a data transfer object and returning an internal response object that packages a Boolean answer and an error message. As you can see, two distinct pieces of HTML are being returned in case of error and success. The only reason to fork between PartialView and PartialViewWithError is to ensure an error HTTP code is returned which would trigger a different JavaScript processing on the client. Here are some typical JavaScript handlers for an Ajax form operation:

function handleFailure(context) {
    var markup = jQuery.trim(context.get_data());
    fnSetFeedback(markup);
    setTimeout(clearFeedbackAfterFailure, 4000);
}

function clearFeedbackAfterFailure() {
    fnSetFeedback("");
}
function handleSuccess() {
    setTimeout(clearFeedbackAfterSuccess, 4000);
}

function clearFeedbackAfterSuccess() {
    fnSetFeedback("");

    // Clear form (if required)
    :

    // Refresh any underlying UI (if required)
    $.ajaxSetup({ cache: false });
    $("#SomePieceOfUI").load("/refresh");
}

Function handleFailure will execute in case an HTTP error code is received. It first updates the current page with the error markup (typically an error icon plus some text) and then sets up a timer. The timer expires after a few seconds (4 seconds in the code) and then clears the error markup. The net effect is that the user has read that something went wrong and is back editing the form for another attempt.

Function handleSuccess will run if the back end operation completed successfully and all we want is giving confirmation to the user. A difference that exists in how ASP.NET MVC handles success and failure is that in case of success, the UI is updated by the framework; in case of failure, it’s our responsibility to display some markup. That’s why handleSuccess just sets up the timer—the UI is already refreshed when it gets called.

Having completed an operation, you probably need to dismiss some UI elements when the timer expires. In first place, you remove the confirmation message from the UI and then you clear the form, hide it or whatever is required. Finally, you may need to refresh some other parts of the UI based on the results of the just completed Ajax operation. This may require another direct Ajax call to some ASP.NET MVC method that just returns fresh data. Let me give you a concrete example. Imagine you have a list of records and then the user clicks and edits one. Imagine also you allow editing using a modal dialog box that hosts an Ajax form. You display the confirmation message in the dialog box and then when the timer expires, you close the dialog. It is likely however that you also need to update the underlying list of records to reflect changes. Another Ajax call may be necessary to grab updated data and refresh the list.

Summary

Today, writing forms that take advantage of Ajax for posting is not hard in ASP.NET. You have partial rendering in Web Forms and you have the Ajax helper object in ASP.NET MVC. Creating Ajax forms in MVC, however, poses issues such as validation and notifications. Notifications are especially a new problem arisen with Ajax that requires some new solution.

 

About the Author

Dino is the author of "Programming ASP.NET MVC" for Microsoft Press and also coauthored the bestseller "Microsoft .NET: Architecting Applications for the Enterprise" (Microsoft Press 2008). A long time author and experienced consultant and trainer, Dino lives in Italy (when not traveling) and plays tennis (when not injured).