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:

When the user navigates to the registration
page:

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

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

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.
Join the conversation! Your thoughts help the community grow.