ASP.NET MVC 3: Using jQuery .ajax() function to submit Ajax Form supporting (unobtrusive) Client Side Validation and Server Side Validation

Update 2011-08-09: I’ve written a follow-up blog post which shows how to use JSON to customize the way the server’s response is handled on the client side.

This blog post summarizes how to use jQuery’s .ajax() function to submit an Ajax form in ASP.NET MVC web applications. In ASP.NET MVC you can use the Ajax.BeginForm(…) helper method to create Ajax forms but the options to customize the behavior of the form are limited. In contrast, using jQuery’s .ajax() function one gets full control about the user interface, the way the data is transfered to the server, the error handling and so forth. In this blog post we will use this flexibility to add a progress jQuery UI dialog that is shown while the request is processed by the server.

We will start with the source code created in my previous post “ASP.NET MVC 3: Ajax Form with jQuery validate supporting (unobtrusive) Client Side Validation and Server Side Validation” which can be downloaded from here. It is recommended to read the mentioned previous post to understand how this Ajax form and the validation should work. In the following we will replace the default ASP.NET MVC Ajax functionality with a customized jQuery alternative which will also support client side and server side validation.

The (server) source code remains nearly unchanged, we only add a Thread.Sleep(2000); to the HomeControllersForm() method to fake the time needed by the server to process the request. This ensures, that the progress dialog will be visible and will not disappear immediately after becoming visible since actually the server only needs some milliseconds to process the request.

First, we will replace the Ajax.Html() helper with Html.BeginForm() in Partial/_Form.cshtml view and add the progress dialog to the Index.cshtml view:

<div id="ProgressDialog" style="text-align: center; padding: 50px;">
    <img src="@Url.Content("~/Content/ajax-loader.gif")" width="128" height="15" alt="Loading" />
</div>

To support jQuery UI we need to add a reference to Scripts/jquery-ui.js in the _Layout.cshtml view:

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

In a last step we add with some lines of JavaScript code to the Index.cshtml view that implements all the Ajax magic:

<script type="text/javascript">
  $(function () {
 
    // Initialize progress dialog ...
    $("#ProgressDialog").dialog({
      autoOpen: false,
      draggable: false,
      modal: true,
      resizable: false,
      title: "Loading",
      closeOnEscape: false,
      open: function () { $(".ui-dialog-titlebar-close").hide(); } // Hide close button
    });
 
    // Handle form submit ...
    $("form").live("submit", function (event) {
      event.preventDefault();
      var form = $(this);
      $("#ProgressDialog").dialog("open");
      $.ajax({
        url: form.attr('action'),
        type: "POST",
        data: form.serialize(),
        success: function (data) {
          $("#FormContainer").html(data);
          $.validator.unobtrusive.parse("form");
        },
        error: function (jqXhr, textStatus, errorThrown) {
          alert("Error '" + jqXhr.status + "' (textStatus: '" + textStatus + "', errorThrown: '" + errorThrown + "')");
        },
        complete: function () {
          $("#ProgressDialog").dialog("close");
        }
      });
    });
  });
</script>

Basically, this JavaScript does two things:

  1. The progress dialog is initialized with some custom options (disable dragging, using modal mode, disable close button, …).
  2. Using jQuery’s live functionality an event handler is registered to intercept the form’s submit event to show the progress dialog, post the form data to the server and hide the progress dialog again when the request is processed.

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

Comments

  1. mark

    Is it possible to check from the Javascript if the unobtrusive client side validation has detected an error and then not submit the form?

  2. Jan Jonas

    Hi Mark,
    you can call valid() to trigger client side validation manually to validate the form and test if the result is true.

    var isValid = $("form").valid();
    if (isValid) {
      // Submit form
    }

    If you run the example source code you will see that jQuery validate already takes care about this and suppresses the postback to the server when client side validation fails. Of course there might be situations where you want to trigger client side validation manually to test if there are any validation errors. Hope this helps …

  3. steve

    Very much appreciate the contribution – however…the sample code would be much better if it avoided using the name ‘form’ for the name of the form. Makes for ambiguity in how/where to replace the string ‘form’ with ‘myForm’.

    thx

  4. Jan Jonas

    Hi Steve,
    thanks for your comment. I assume that you are talking about the JavaScript variable “form” and the code line

    var form = $(this);

    I think you don’t have to change this to something like “myForm”. The jQuery selector

    $("form").live("submit", function (event) {
      [...]

    selects all form tags on the current page and initializes the submit event handling. The “form” variable is just used for convenience to refer to the current form tag the jQuery selector is processing.
    Please let me know if I miss something or don’t get your question.

  5. Ananda Sudarshan (Anand)

    Awesome blog post.. thanks a lot, such a lucid explanation to a very critical topic

  6. Anton

    Thank you for your post!
    Your solution works perfectly when using submit button, but I have to submit a form on checkbox click. Something like this:

    @using (Html.BeginForm(“Act”, “Contr”, FormMethod.Post))
    {
    @Html.CheckBoxFor(model => model.BoolValue, new { @id = “chk1″ , @onchange = “this.form.submit()” })
    }
    In this case your script is not working, I assumme it should be the same submit event for any control. So what is wrong here?
    Thanks.

  7. Anton

    Ok, I found the answer myself: looks like submit event is not executed im my case, so I just added: $(“form”).trigger(‘submit’) to checkbox click and it works now.

  8. JTBS

    Thanks for details. When I am using your solution in a form that is loaded into DIV by another AJAX call.. submit events are getting added up.

    Example: Once first submit is over, I close the form, dialog, make DIV contents empty.
    Second time I click, I get fresh PartialView HTML -which has the same Form.. and when I submit this time, I get two submits.

    I think DOM model is some how keeping older event handler reference -as I am on same page..

    Is there any way to do proper clean up once all is done.

  9. JTBS

    If it helps… replacing live with bind and “return false;” at the end.. solved my problem.. of multiple events

  10. Jan Jonas

    Hi JTBS,
    I think I do not get your problem in detail, but replacing jQuery’s live() with bind() causes that the submit event handler is only added to all DOM nodes that exist when the (jQuery) JavaScript code is executed (in my example when the DOM is fully loaded). Using live() instead instruct jQuery to also add the submit event handler to all DOM nodes that are added in the future. In my example the <form> tag is replaced when server side validations fails and the event handler needs to be added again; and that is exactly what jQuery’s live() is for.
    Do you have a running example that demonstrates your problems?

  11. Jan Jonas

    Hi Anton,
    Thanks for your comment. AFAIK, the form’s onSubmit event is only fired if the submit action is triggered by the user (e.g. by clicking a submit button) and not if the form is submitted by some JavaScript code (as in your example). I think that is the reason why your first code does not work.
    Moreover, I would recommend to use

    $(this).submit()

    instead of

    $('form').trigger('submit')

    because this code triggers the submit event of all forms in the DOM (which is of course not a problem if you only have one form).

  12. Anton

    Hi Jan,
    thanks for your comment. I had one form there, but suggestion is good anyway.

  13. Ben

    I did something similar and it works but if you have any complex objects ( eg child tables etc) , serialize fails .. any ideas. Sure i can use json but thats a lot of code and doesnt work well with the errors

  14. Fouz

    please i have to load a JSON object recupered with .post() on a formular without using .each(). how can i do

    $.post(“/Home/Edit”, { “id”: id },function (data) {
    ???????
    }

    my form is partiel view and it is in tag

  15. Fouz

    my Form is in tab Div
    my formId is FormExperience
    my DivId is Form

  16. Jan Jonas
  17. lisa

    The sample solution does neither work nor really compile…

    In the markup I get overall razor errors that certain Html methods does not exist.

    When I press F5 to run it then no dialog is shown always a view embedded the website.

    Please fix that thanks.

  18. Jan Jonas

    Hi Lisa, could you please send me the detailed error message you get in the Razor views? I’ve downloaded the source code and it works fine. Maybe you do not have ASP.NET MVC3 installed on your development machine?
    Hope this helps…

  19. Laurent

    Hello,
    This is a great example, but I wonder why it doesn’t work with file upload.

    I added an [code][/code] in index.cshtml, and an extra parameter in the Form method of the controller, which now looks like this: [code]public ActionResult Form(ValidationModel validationModel, HttpPostedFileBase file)[/code]

    This works normally fine on any basic form, but it doesn’t work anymore with your js functions. Is it because AJAX prevents file upload?

  20. Laurent

    Oops, my [code] tags didn’t work.

    The first one is :

    Sorry for the inconvenience.

  21. Jan Jonas

    Hi Laurent,
    you cannot submit a file with an AJAX request. You have to submit the file with another “normal” POST request. Implementing file uploads with the “AJAX look & feel” is a bit tricky and require some workarounds (you can for example us an iframe to submit the file POST request to prevent a complete page reload). If I find the time I will add a new blog post that shows how an implement a file upload in an AJAX form.

  22. David

    I try it in my application (using my already loaded jscripts), and when it gets to the $.validator.unobtrusive line, I get the following error:
    “Unable to get property ‘unobtrusive’ of undefined or null reference”

    Any ideas?

  23. Jan Jonas

    Hi David,
    thanks for your comment. Have you tried this http://stackoverflow.com/a/10757877?
    Hope this helps…

  24. Manikandan S

    i am getting an error(Microsoft JScript runtime error: Object doesn’t support this property or method) while using/executing the below script,

    $(“#ProgressDialog”).dialog({
    autoOpen: false,
    draggable: false,
    modal: true,
    resizable: false,
    title: “Loading”,
    closeOnEscape: false,
    open: function () { $(“.ui-dialog-titlebar-close”).hide(); } // Hide close button
    });

    Please help me on this….

  25. Jan Jonas

    Hi Manikandan,
    Thanks for your comment. Please make sure, you have included Scripts/jquery-ui.js to enable jQuery UI support.
    Hope this helps…

Leave a comment

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

*