ASP.NET MVC 3: Ajax Form with jQuery validate supporting (unobtrusive) Client Side Validation and Server Side Validation

Update 2011-08-07: I’ve written a new follow-up blog post to this one which shows how to upgrade the techniques described here with custom (jQuery) JavaScript code to implement a more flexible Ajax form handling.

This blog post shows step by step instructions how to build an Ajax form with jQuery validate supporting (unobtrusive) client and server side validation using ASP.NET MVC 3.

Summarized the (Ajax) form will have the following features:

  1. Client side validation: Before submitting the data to the server the client validates the data to reduces the number of requests and to improve usability.
  2. Server side validation: The server validates the data again to find malicious requests that bypasses client side validation and to perform additional validations that cannot be done on client side, e.g. validations that need database access.
  3. Partial page updates: If server side validation fails the error messages are injected in the DOM on the client side to avoid complete page reloads.

To follow the steps below you can start from a new (empty) ASP.NET MVC 3 Web Application with Razor support or you can download the Visual Studio 2010 project containing all the source code here.

To enable (unobtrusive) client side Ajax and validation support include the following JavaScript files (in Views/Shared/_Layout.cshtml):

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

and make sure, that ClientValidationEnabled and UnobtrusiveJavaScriptEnabled is enabled in your Web.config (which should be the default values):

<appSettings>
  <add key="ClientValidationEnabled" value="true"/>
  <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>

Next, we create a Model (Models/ValidationModel.cs) which defines a property with some validation attributes:

public class ValidUserNameAttribue : ValidationAttribute
{
  public override bool IsValid(object value)
  {
    return (value != null && value.ToString() == "Bob");
  }
}
 
public class ValidationModel
{
  [Required]
  [StringLength(8, MinimumLength = 3)]
  [ValidUserNameAttribue(ErrorMessage = "User name != 'Bob'")]
  [Display(Name = "User Name")]
  public string UserName { get; set; }
}

The custom ValidUserNameAttribue adds a validation that is only performed on server side. In a real world example this attribute could for example query a database to ensure the user name is unique. In this example only “Bob” is accepted as a valid user name. Both other attributes (Required and StringLength) trigger validation on client and on server side.

The (minimalistic) HomeController defines two actions: Index to render the form and Form to validate the request and render the result.

public class HomeController : Controller
{
  [HttpGet]
  public ActionResult Index()
  {
    return View(new ValidationModel());
  }
 
  [HttpPost]
  public ActionResult Form(ValidationModel validationModel)
  {
    return PartialView(!ModelState.IsValid ? "Partial/_Form" : "Partial/_Success", validationModel);
  }
}

In a last step we need to define the views. As you can see in the HomeController‘s Form action we will use partial views for the form and the success message to enable the partial page updates on the client side.

Home/Index.cshtml:

@model MVC3_Ajax_Form_jQuery_Validation.Models.ValidationModel
 
@{
    ViewBag.Title = "ASP.NET MVC 3: Ajax Form with jQuery validate supporting Unobtrusive Client Validation and Server Side Validation";
}
 
@DateTime.Now: Index.cshtml rendered
<hr/>
 
<div id="FormContainer">
    @Html.Partial("Partial/_Form")
</div>

The Index view defines a <div> tag that is used as form container, i.e. it’s content is replaced with the server’s response. If the server side validation fails the server sends HTML code of the form containing server side validation error messages, otherwise HTML code containing a success message.

Home/Partial/_Form.cshtml:

@model MVC3_Ajax_Form_jQuery_Validation.Models.ValidationModel
 
@DateTime.Now: Partial/_Form.cshtml rendered
<hr/>
 
@using (Ajax.BeginForm("Form", new AjaxOptions() { UpdateTargetId = "FormContainer" , OnSuccess = "$.validator.unobtrusive.parse('form');" }))
{
 
    <p>
        Error Message: @Html.ValidationMessageFor(m => m.UserName)
    </p>
 
    <p>
        @Html.LabelFor(m => m.UserName):
        @Html.EditorFor(m => m.UserName)
    </p>
 
    <input type="submit" value="Submit" />
}

The Partial/_Form.cshtml partial view defines the (Ajax) form. This partial view is included with @Html.Partial()in the Index view for the initial page load and used by the HomeController‘s Form action to render the form with server side validation messages.
It’s important to call $.validator.unobtrusive.parse(‘form’); in the OnSuccess callback to reinitialize the client side validation after the form container’s HTML content is replaced!

Home/Partial/_Success.cshtml:

@model MVC3_Ajax_Form_jQuery_Validation.Models.ValidationModel
 
User name '@Model.UserName' is valid :)

The Partial/_Success.cshtml partial view defines the success message that is used to replace the form after the request passes server side validation.

Both the view Home/Index.cshtml and Home/Partial/_Form.cshtml render the current time-stamp that shows that the partial page update is working: After submitting a user name unequals ‘Bob’ only form render time-stamp changes.

You can download the Visual Studio 2010 project containing all the source code here.

Comments

  1. William Davies

    Thanks for that, ,first example on the web that I have founf that actually works. Much love.

  2. Tim Collinson

    Thanks! Your three posts in this series are great. Well worth the read.

  3. Jan Jonas

    You’re welcome :)

  4. Guimaraes Mahota

    I doubt in the following three mvc ajax (asp.net, jquery and Partial Views).

    I want to update a table (add, delete, update and save), using ajax.

    Adapted the tutorial up there for my needs.

    Clicking a button (eg, add table) works on some tables more in the other if the following error:

    The script on this page May Be Busy, or It May have stopped responding. Can you stop the script now, or you can proceed to see if the script will complete.

    Script: http://localhost:4951/Scripts/Wijmo-Open/development-bundle/external/jquery-1.5.2.min.js:16

  5. Kris

    Good job … you created good example that you can use in production enviroment.

  6. ayaz

    help

  7. Stephane

    clear, concise, easy to follow.
    downloaded sample code, unzipped, compiled and ran — stepped through with debugger to fully grasp — everything worked perfectly.

    This post has finally solved my numerous stabs at the issue

    Thanks!

  8. Will

    I’m building an application using jQuery UI tabs and form validation using AJAX. I wish to achieve the user authentication, then have it show the other tabs of the application upon authentication success. But then, I finally found your post with form validation which is actually what was missing to get the gist of it.

    Thanks for this useful post, I love it! And be sure I’ll follow the other related articles too so that I can fully grasp the different aspects presented.

    Thanks again! =)

  9. Sheir

    I am new to MVC3 + Razor + jQuery + Ajax.
    I have a viewModel that my View is based on and am trying to do an ajax callback to my Controller when user clicks on a button (ie. clear grid rows).
    Manage to get Controller method being called (after removing the httppost attribute from method).
    public ActionResult ClearAllRows(ManageJobsViewModel model)
    {
    }
    Problem is that values in my model are null, except for one had changed.
    But the grid is strictly display only, so its rows do not change and so it seems I get back a null for my viewmodel collection property.

    How can I force the data being sent from ajax to be a fully populated viewModel?

  10. Jan Jonas

    Hi Sheir,
    If you would like to use the approach presented in the blog post for a grid view, I would suggest to create a partial view that renders the grid view (like Home/Partial/_Form.cshtml for the form in the example) and your ClearAllRows() action should return this partial view. On the client side you need to update the grid by replacing the grid’s DOM node with the DOM of the rendered partial view that is returned from the server.
    I would recommend to take a look at part 2 and part 3 of my blog posts to get an idea about implementing AJAX in ASP.NET MVC. Please keep in mind that the blog posts are focused on validation and handling form data and the approach could not be used one-to-one for grid views.
    Hope this helps …

  11. sheir

    Thanks.
    Now my form has multiple submit buttons and I have used a special custom attribute I found via some article I found on google search.
    So normal non-ajax way works nicely..postback..entire page refresh/reload.

    Now I have been trying all weekend to ajax-ify the buttons and NO luck.
    I am extremely tired now and mad frustrated.

    As an example, take the ClearAllRows, I have a function in my home controller and it has the attribute that maps the html form post method (aka managejob) to it from that submit button. In normal postback, it all works.

    When I tried ajax the button, with url of home/clearallrows; it blows up and I cannot figure it out. Like I had mention, I am new to this world.

    Very tired and frustrated. Can you hellp with the ajax-ify of my multiple submit buttons?

  12. Sheir

    Sorry forgot to get back to you. Manage to solve the issue — ajaxify of my form with the multiple submit buttons.
    What I had to do was create a method in my controller (tagged with HttpPost attribute) and this method would then just call my normal postback functions.

    As an example: This is my normal postback Controller method for the ClearAll.

    [HttpPost]
    [SubmitActionName(ActionName="ManageJob", SubmitButton = "btClearAllRows")]
    [ValidateAntiForgeryToken(Salt="Blog")]
    public ActionResult ClearAllRows(MyViewModel e)
    {
    //do stuff
    if(Request.IsAjaxRequest())
    {//more stuff
    return Json(someObject, JsonRequestBehavior.AllowGet);
    }
    //fallback
    return RedirectToAction(“Index”);
    }

    [HttpPost]
    public ActionResult AjaxClearAllRows(MyViewModel e)
    {
    return ClearAllRows(e);
    }

    In my view, I had code such as:
    $(“#btClearAllRows).on(“click”, function (event) {
    event.preventDefault();
    $(#ProgressDialog”).show();
    var formToSubmit = $(‘form:first’);
    var param = $(formToSubmit).serialize();
    var lnk = ‘@Url.Action(“AjaxClearAllRows”, “Home”);

    $.ajax({
    url: lnk,
    type: “POST”,
    dataType: “json”,
    context: document.body,
    data: param,
    cache: false,
    success: AjacClearAllSuccess,
    error: AjaxError,
    complete: AjaxComplete
    }).
    });

    So in my AjacClearAllSuccess I would just replace a div with the data I got from the controller method (ie a partial view rendered to string and passed as json). Example:
    function AjacClearAllSuccess (data, textStatus, jqxHR) {
    $(“#myPVdiv”).html(data);
    }

    It works nicely.

  13. Rob Pearmain

    Many thanks for this article, I was pulling my hair out trying to get this to work, the missing secret was:

    $.validator.unobtrusive.parse(‘form’);

    Thanks again

Leave a comment

Your email address will not be published. Required fields are marked *

*