Using Unobtrusive Ajax Forms in ASP.Net MVC3


This post covers how to use the new unobtrusive libraries in ASP.net MVC3 to provide an improved form submission user experience utilising AJAX. The idea is to have the whole form contents submitted and then replaced by the server generated content, be it either the submitted form with validation errors, or a success message.

First, you need to ensure that you reference the required javascript - jquery.unobtrusive-ajax.js (or min.js for production).

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>

Each Ajax form requires at least two views. The first view is a wrapper around the actual form content, while second view is a partial view that contains the form itself. To demonstrate what I mean, I have created a simple registration form (note how it used the new IValidatableObject interface for complex model validation at binding):

namespace Registration.Models
{
  public class RegistrationFormModel: IValidatableObject
  {
    [Required]
    public string Firstname
    {
      get;
      set;
    }

    [Required]
    public string Surname
    {
      get;
      set;
    }

    [Required]
    public int Age
    {
      get;
      set;
    }

    public string RegsitrationUniqueId
    {
      get;
      set;
    }

    #region IValidatableObject Members

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){
      if (Age < 21){

        yield return new ValidationResult("Age must be at least 21 years.", new string []{ "Age" });

        yield return new ValidationResult("Age must be at least 21 years.", new string []{ "Age" });
    }
    #endregion
  }
}

 

The outer form is just a container that holds the dynamic content, be it the data entry form or the success message. I've included the current date time to demonstrate how the outer wrapper does not change when the inner form content is submitted.

    @{
    ViewBag.Title = "MyForm";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Register Online - @DateTime.Now.ToString()</h2>

<div id="formContent">
    @{Html.RenderPartial("FormContent");}
</div>

 As you can see, it merely renders the content of the partial view inside an element that becomes the target of our ajax post result. The form content, for the purpose of this blog, is trivial. Note how I have used the Post option in the AjaxOptions – this is because I want data submitted to the Index() overload of the controller that accepts HttpPost.

@model Registration.Models.RegistrationFormModel

@{
    AjaxOptions options = new AjaxOptions{
        HttpMethod = "Post",
        UpdateTargetId = "formContent"        
    };        

}

@using (Ajax.BeginForm(options)) {    
  <fieldset>
    <legend>Registration Form</legend>
    @Html.ValidationSummary(true)

    <div class="editor-label">
      @Html.LabelFor(model => model.Firstname)
    </div>
    <div class="editor-field">
      @Html.EditorFor(model => model.Firstname)
      @Html.ValidationMessageFor(model => model.Firstname)
    </div
>

    <div class="editor-label">
      @Html.LabelFor(model => model.Surname)
    </div>
    <div class="editor-field">
      @Html.EditorFor(model => model.Surname)
      @Html.ValidationMessageFor(model => model.Surname)
    </div> 

    <div class="editor-label">
      @Html.LabelFor(model => model.Age)
    </div>
    <div class="editor-field">
      @Html.EditorFor(model => model.Age)
      @Html.ValidationMessageFor(model => model.Age)
    </div
>

    <div>
      <input type="submit" value="Register" />
    </div>
  </fieldset>
}

I have added an additional partial view which will be rendered when the form is successfully submitted:

@model Registration.Models.RegistrationFormModel

<h3>You have successfully registered.</h3>

<p>Your regsitration number is @Model.RegsitrationUniqueId</p>

The controller for this form for the purposes of this demo is pretty simple. If the model is valid then a registration is calculated but whatever means and the Success view is returned. If the model is invalid (becuiase the IValidatableObject interface has returned a model error, then the data-entry form is returned back to the user to fix their errors.

public class FormController : Controller
{

    public ActionResult Index()
    {
        return View(new RegistrationForm());
    }

    [HttpPost]
    public PartialViewResult Index(RegistrationForm model)
    {
        if (ModelState.IsValid)
        {
            //go and do registration business logic,
            RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();

            byte[] regsitrationBytes = new byte[16];
            csp.GetBytes(regsitrationBytes);
            model.RegsitrationUniqueId = Convert.ToBase64String(regsitrationBytes);
            return PartialView("Success", model);
        }
        else
        {
            //return the data entry form
            return PartialView("FormContent", model);
        }
    }
}
 

The project layout is as follows:

2.png
 

When the user navigates to the registration page:

3.png
 

When the user attempt to register a user who is too young, server side validation kicks in and the data entry form is returned:

4.png
 

When the data is correctly entered, the success message is displayed:

5.png
 

Note how in all the images the date/time that the registration startd has not changed – there has been no full post back to the server.

Of interest is how the unobtrusive ajax is rendered to the client – without masses of javascript but rather HTML5 valid element attributes on the form element as follows:

<form action="/PVA/Form/" data-ajax="true" data-ajax-method="Post" data-ajax-mode="replace" data-ajax-update="#formContent" id="form0" method="post">
...
</form>

Conclusion

This post has demonstrated how easy it is to provide a superior user experience using Ajax forms and the unobstrusive javascript libraries in ASP.Net MVC 3. You can visit more interesting topic about ASP.NET MVC 3 at here


Similar Articles