ASP.NET MVC2 AJAX: Executing Dynamically Loaded JavaScript

(or ASP.NET MVC2 AJAX: ASP.NET MVC2 Client Validation in an AJAX Loaded Partial View)

I have been tinkering with ASP.NET MVC2 for a while and I had the problem where the MVC2 Client Validation did not work when I was dynamically loading a partial view through MVC2 AJAX. Upon further investigation, I discovered that the issue was not limited just to MVC2 Client Validation, but to all JavaScript that is dynamically loaded through MVC2 AJAX. The issue has to do with the way that the response is injected into the DOM element – through the InnerHTML property. Any script block injected into that property will not be executed.

Note: when I refer to the ASP.NET MVC2 AJAX, I am specifically referring to the usage of method calls such as “Ajax.ActionLink()” and “Ajax.BeginForm()”.

There are many blogs and tutorials that discuss how to load partial views asynchronously with AJAX in ASP.NET MVC2, and there are even more articles on how do that with jQuery. Unfortunately most of these deal with the simplistic scenario of explaining the basics of partial views.

It is difficult to find any information on how to do MVC2 Client Validation (or use any JavaScript) from within a partial view that is loaded dynamically with ASP.NET MVC2 AJAX. There are however a number of forum posts with people questioning exactly how to do this!

Now, I am not going to go into discussions about how it might be better practice to put all the JavaScript in separate .js files and load those when the page is initially loaded. While I agree, there are times when that is simply not an option… MVC2 Client Validation is one such example, as it emits JavaScript when the partial view is rendered.

Side note: I originally came across this same type of behaviour is ASP.NET Web Forms in 2007, with the AjaxControlToolkit UpdatePanel. A workable solution for ASP.NET Web Forms is described here: http://weblogs.asp.net/infinitiesloop/archive/2007/09/17/inline-script-inside-an-asp-net-ajax-updatepanel.aspx.

Below are some links to various posts by other people who have had similar issues.

In summary, the responses essentially say three things:

1. ASP.NET MVC AJAX will not execute JavaScript that is dynamically loaded into a DOM element;

2. The only workable solution is to not use MVC AJAX and instead write JavaScript and use jQuery to do all the AJAX; and

3. ASP.NET MVC2 Client Validation needs to be reinitialised after a partial view is dynamically loaded and the JavaScript is executed.

I found these responses to be unsatisfactory! Are people seriously suggesting that one does all of that in the AjaxOption.OnSuccess delegate every time? Right now, I want to take advantage of MVC2 Client Validation and I want to use MVC AJAX – because it is simpler than the alternatives. It should ‘just work’, shouldn’t it? Am I asking too much? After all, this is the second version of ASP.NET MVC!

So noting that JavaScript is a dynamic language, I created the following solution which seamlessly extends the ASP.NET MVC2 JavaScript to make the execution of dynamically loaded JavaScript and the reinitialisation of MVC2 Client Validation happen automatically.

Without diving deeply into the code, the approach is to override the MicrosoftMvcAjax.js Sys.Mvc.MvcHelpers._onComplete method so that I can then hook into the ajaxOptions.onSuccess delegate in order to use jQuery.globalEval() to execute the JavaScript dynamically loaded into the target element, and then finally reinitialise the MVC2 Client Validation.

Just by including this script in an application (carefully loaded AFTER the jQuery and MicrosoftMVCValidation scripts!), this will now happen seamlessly on every MVC2 AJAX request.

Yay! Now the dynamically loaded JavaScript executes, and the MVC2 Client Validation works on partial views that are loaded dynamically with MVC AJAX!

AjaxLoadedContentScriptFix.js

////////////////////////////////////////////////////////////////////////////////
//
// Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix
//
// Original Author: Adam M Craven (http://adammcraventech.wordpress.com)
//
// This extension to ASP.NET MVC2 makes any script loaded as part of dynamically loaded content
// be executable and reinitialises the MVC2 client validation to enable that processing to occur
// for an AJAX loaded view.
//
// Warning: This script may not work with future versions of the minimised MicrosoftMvcAjax.js.
//
// When content (e.g. Partial Views) is dynamically loaded into a DOM target element through AJAX
// via the standard ASP.NET MVC2 "Ajax.ActionLink()" or "Ajax.BeginForm()" methods, any script
// (such as javascript or MVC client validation script) that was emitted is not executed because
// the content is assigned to the target element’s "innerHTML" property. This will not cause the
// script to be executed or execuatable.
//
// Must be included after jQuery and the MicrosoftMvcValidation javascript, typically like this:
//    <script type="text/javascript" src="<%=Url.Content("~/Scripts/jquery-1.4.1.min.js") %>"></script>
//    <script type="text/javascript" src="<%=Url.Content("~/Scripts/MicrosoftAjax.js") %>"></script>
//    <script type="text/javascript" src="<%=Url.Content("~/Scripts/MicrosoftMvcAjax.js") %>"></script>
//    <script type="text/javascript" src="<%=Url.Content("~/Scripts/MicrosoftMvcValidation.js") %>"></script>
//    <script type="text/javascript" src="<%=Url.Content("~/Scripts/AjaxLoadedContentScriptFix.js") %>"></script>
//

Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix = function Sys_Mvc_MvcHelpers_AjaxLoadedContentScriptFix() {
}

Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._onComplete = function Sys_Mvc_MvcHelpers_AjaxLoadedContentScriptFix$_onComplete(request, ajaxOptions, ajaxContext) {
    /// <param name="request" type="Sys.Net.WebRequest">
    /// </param>
    /// <param name="ajaxOptions" type="Sys.Mvc.AjaxOptions">
    /// </param>
    /// <param name="ajaxContext" type="Sys.Mvc.AjaxContext">
    /// </param>

    // Hook into the ajaxOptions.onSuccess delegate
    ajaxContext._ajaxLoadedContentScriptFixOrigAjaxOptionsOnSuccess = ajaxOptions.onSuccess;
    ajaxOptions.onSuccess = Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._ajaxOptionsOnSuccess;

    // Call the original MVC onComplete method
    Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._origOnComplete(request, ajaxOptions, ajaxContext);
}

Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._ajaxOptionsOnSuccess = function Sys_Mvc_MvcHelpers_AjaxLoadedContentScriptFix$_onSuccess(ajaxContext) {
    /// <param name="ajaxContext" type="Sys.Mvc.AjaxContext">
    /// </param>

    // Make any dynamically loaded script execute
    Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._globalEvalScriptInElementId(ajaxContext.get_updateTarget());

    // Reinitialise the MVC validation
    Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._reinitialiseMvcValidation();

    // Call the original success delegate
    if (ajaxContext._ajaxLoadedContentScriptFixOrigAjaxOptionsOnSuccess) {
        ajaxContext._ajaxLoadedContentScriptFixOrigAjaxOptionsOnSuccess(ajaxContext);
    }
}

Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._globalEvalScriptInElementId = function GlobalEvalScriptInElementId(element) {
    if (jQuery) {
        // jQuery.globalEval($("#" + element.id).find("script").text());

        // It seems jQuery 1.4.1 & 1.4.2 has a problem in IE with .text() on script nodes… so do the loop ourselves…
        var scripts = $("#" + element.id).find("script");
        var allScriptText = "";
        for (var i = 0; i < scripts.length; i++) {
            allScriptText += scripts[i].text;
        }
        jQuery.globalEval(allScriptText);

    } else {
        alert("Error: jQuery must be loaded in order to use Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix");
    }
}

Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._reinitialiseMvcValidation = function Sys_Mvc_MvcHelpers_AjaxLoadedContentScriptFix$ReinitialiseMvcValidation() {
    if (Sys.Mvc.FormContext) {
        Sys.Application.remove_load(arguments.callee);
        Sys.Mvc.FormContext._Application_Load();
    }
}

// Register this extension
Sys.Application.add_load(function () {

    if (typeof (Sys.Mvc) === 'undefined' || typeof (Sys.Mvc.MvcHelpers) === 'undefined' ||
        (!Sys.Mvc.MvcHelpers._onComplete && !Sys.Mvc.MvcHelpers.$3)) alert("Error: MicrosoftAjax and MicrosoftMvcAjax.js (or their debug versions) must be loaded in order to use Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix");

    var isMicrosoftMvcAjaxDebugJs = Sys.Mvc.MvcHelpers._onComplete;

    if (isMicrosoftMvcAjaxDebugJs) { // if using MicrosoftMvcAjax.Debug.js
        Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._origOnComplete = Sys.Mvc.MvcHelpers._onComplete;
        Sys.Mvc.MvcHelpers._onComplete = Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._onComplete;
    } else { // using MicrosoftMvcAjax.js
        Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._origOnComplete = Sys.Mvc.MvcHelpers.$3;
        Sys.Mvc.MvcHelpers.$3 = Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._onComplete;
    }
});

Note: this same technique could be used to hook into the other AjaxOption delegate methods if there was a standard, application-wide action that should take place on each AJAX operation… oh I don’t know… for instance: error handling… or an animation…

Update 05/07/2010 – fixed an issue where the script only worked when using the MicrosoftMvcAjax.Debug.js and did not work when using MicrosoftMvcAjax.js. Warning: this script may not work with future versions of the minimised MicrosoftMvcAjax.js.

Update 07/07/2010 – slight tweak to make the check for Sys.Mvc.MvcHelpers happen in the load, and not before we actually need to do it. Some older browsers load the js files out of order and the alert was unnecessarily showing.

57 Responses to ASP.NET MVC2 AJAX: Executing Dynamically Loaded JavaScript

  1. Shivi says:

    Awesome post! Exactly what I was searching for !! Thank you. I will implement it and post here my experience.

  2. Shivi says:

    I have tried your solution but the client-side validation still doesn’t work for me.

    I have included the “js” file above in my code. This time the javascript something like :
    “if (!window.mvcClientValidationMetadata) { ..” loads into the “div” specified by “UpdateTargetId” option of AjaxOptions in a “Ajax.ActionLink()” call. But the script hasn’t executed for me. The submit still posts back to the server for validation and errors are populated from the server. The client-side validation script is loaded but isn’t working.

    Maybe there is something wrong in the parsing of script tags and executing it.

    Browser: Firefox 3.6.3 IDE: VS 2010 JQuery version: 1.4.1.min

    Please let me know some solution. I really need to fix this out.

    • Adam Craven says:

      Hi,

      I don’t know what is going wrong in your circumstance. Perhaps if you post a minimal version of your code with my solution I may be able to help diagnose the issue.

      regards
      Adam

  3. Shivi says:

    Finally I got it to work!

    I used the approach in this great blog post by tpeczek:

    http://tpeczek.blogspot.com/2010/04/making-aspnet-mvc-2-client-side.html

    It worked very well for me.

  4. Shivi says:

    Here’s the code in my view:

    As per tpeczek:

    As per you, I removed the OnSuccess.

    The “div” with id=”ReportAbuse” is in the same view.

    I have included your script in my view. The action “ReportAbuse” returns an Ajax.BeginForm(). Here is the minimized code for it:

    MvcMusicStore Copyright Infringement Notification
    .
    .
    .

    The form renders correctly within the view but the javascript in the partial view “ReportAbuse.ascx” doesn’t executes. I have a javascript to open the “” in a modal dialog box. This javascript also doesn’t executes and the form loads in the current view itself rather than opening up in a form.

    The actual problem lies in executing the javascript generated by the mvc validation script as well as the one I wrote for modal popup.

    I tried debugging your script in FireBug, but it seems that your script is never executed. The firebug doesn’t hit the breakpoint I set in your script.

  5. Shivi says:

    The code is being parsed by the “submit comment”. Let me try re-submitting it with tags removed. You’ll see code below if your comment parser allows it :)

    Here’s the code in my view:

    As per tpeczek:

    Ajax.ActionLink(“Report Abuse”, “ReportAbuse”, new { id = track.Id }, new AjaxOptions { UpdateTargetId = “ReportAbuse”, HttpMethod = “Get”, OnSuccess = “Sys.Mvc.FormContext.OnSuccessEnableClientValidation” }, new { id = track.Id })

    As per you, I removed the OnSuccess.

    Ajax.ActionLink(“Report Abuse”, “ReportAbuse”, new { id = track.Id }, new AjaxOptions { UpdateTargetId = “ReportAbuse”, HttpMethod = “Get”, OnSuccess = “Sys.Mvc.FormContext.OnSuccessEnableClientValidation” }, new { id = track.Id })

    I have included your script in my view. The action “ReportAbuse” returns an Ajax.BeginForm(). Here is the minimized code for it:

    Html.EnableClientValidation();

    using (Ajax.BeginForm(new AjaxOptions{ UpdateTargetId = “ReportAbuse” })) {

    Html.ValidationSummary(true, “Please fill in the required fields.”)

    .
    .
    . HTML Markup here..
    .

    }

    The form renders correctly within the view but the javascript in the partial view “ReportAbuse.ascx” doesn’t executes. I have a javascript to open the “” in a modal dialog box. This javascript also doesn’t executes and the form loads in the current view itself rather than opening up in a form.

    The actual problem lies in executing the javascript generated by the mvc validation script as well as the one I wrote for modal popup.

    I tried debugging your script in FireBug, but it seems that your script is never executed. The firebug doesn’t hit the breakpoint I set in your script.

    • Adam Craven says:

      It sounds like you might have identified the problem – if my script is never executed then that could be the issue.

      Can you please ensure that you are loading my script from the same location where you load the js files for jQuery, MicrosoftAjax, MicrosoftMvcAjax and MicrosoftMvcValidation (possibly in the Master page).

      In addition, please ensure that it is loaded AFTER the MicrosoftMvcAjax and MicrosoftMvcValidation javascript files.

      Then in FireBug, set a breakpoint down the bottom of my script:

      // Register this extension
      Sys.Application.add_load(function () {
      BREAKPOINT ON THIS LINE.

      That line should execute after the DOM has loaded, after the Microsoft scripts have loaded, and therefore hook into the onComplete method of all future MVC AJAX calls.

      Hopefully this will provide us with more information as to why it isn’t working for you.

      regards,
      Adam

      • Shivi says:

        I will try it and let you know. As far as inclusion of your script is concerned, it is getting included(i hv checked this in firebug twice by clearing browser cache). I will set the breakpoint at the place you mentioned and let u know my the result.

  6. Mark says:

    I really like the idea of this approach, but I just can’t get it to work. Firstly let me just see if I am understanding this correctly. After I have included your script in my page (Site.Master) i will be able to wire up events using $(document).ready in the partial view itself, after it is loaded via Ajax.ActionLink? I tried to get it to work by placing this code in my partial :
    $(document).ready(function () {
    alert(‘hello’);
    });

    and it’s never being called? I assume that your fix simply proxies the OnSuccess action, binds any jquery, and then delegates off to the original OnSuccess method?

    I have done some dubugging in firefox and put the breakpoint where you reccomended, but it seems it only hits the breakpoint on first load. When the ajax call is made, it never hits any of my other breakpoints ( placed on nearly every line of your script ).

    Any help or advice would be gratly appreciated.

    P.S. : I have an OnSuccess method wired up and that IS being called.

    • Adam Craven says:

      Hi Mark,

      Hmm, I am troubled as to why a few people are having issues with the script – it works perfectly in my project… so sincerely thank-you for your comment!

      $(document).ready will NOT fire when the partial views have been loaded asynchronously… because the Ajax.ActionLink performs a async call to the server and then receives the response which it subsequently _injects_ into the DOM. The document is not reloaded, and $(document).ready() will not be fired.

      If your view was to include the javascript without the $(document).ready part, it should be executed as part of the onSuccess which yes, I have essentially overridden.

      alert(‘hello’);

      Please let me know if it works… if not, perhaps you can publish your project somewhere so I can take a look.

      regards
      Adam

  7. Mark says:

    Hi Adam, I changed my js includes to use the ‘debug’ versions and the script started working? I’m no expert on MVC but i can only assume the debug scripts are for..debugging and should not be used in live?

    thanks for the help

    • Adam Craven says:

      Interesting… did the $(document).ready work with the debug scripts?

      or did the alert work by itself with the minimal scripts?

      I’m going to run some tests myself…

      • Mark says:

        Funnily enough, the document.ready does work. I have no problem using the ‘debug’ versions to enable me to use this functionality because in my opinion, this is the best improvement to come along since MVC2 itself!!

    • Adam Craven says:

      ok, I have just replicated the issue. It seems that my script is not currently working with MicrosoftMvcAjax.js, but it is working with MicrosoftMvcAjax.Debug.js…

      I will investigate further and post a solution…

    • Adam Craven says:

      Hi Mark,

      Thanks for identifying that my script only worked with the debug versions of MicrosoftMvcAjax.

      The problem was that the minimised version obfuscates the function name that I hook into…

      I have updated the script so that it now works with either the debug or minimised versions…

      regards
      Adam

  8. Mark says:

    Adam… you are a legend!

  9. Jonas says:

    Thanks man, awesome solution!

  10. Neil says:

    Thanking you profusely!! This has been a thorn in my side for days until now!!! Awesome.

    • Adam Craven says:

      Hi thanks for that… But there are a few differences between the problem he is trying to solve and the one I describe.

      Primarily, he is using the synchronous, full postback Html.BeginForm() method instead of MVC’s asynchronous postback Ajax.BeginForm() method.

      This means that he has to write jQuery in each view in order to override the synchronous full postback submit behaviour in order to asynchronously postback each view’s form, and then manually dynamically populate the html of a DOM element.

      In contrast, I don’t need to write any JavaScript in any of views, and I am taking full advantage of MVC’s Ajax API.

      Unlike the way that MVC injects the returned view html into a DOM element, jQuery’s .html function does so in a fashion that also executes the JavaScript, which is why that part works for him.

      I prefer my approach of using MVC’s Ajax.BeginForm() approach and not having to override each submit behaviour with my own JavaScript in each of my views.

      regards
      Adam

  11. Vladekk says:

    Thanks, it seems to work for me. It was quite hard to find why JS is not working. Luckily, I remembered that dynamically added JS has issues, and after some searching found your post.

  12. Ari says:

    Does this work on ASP.NET MVC 1? just tried this on my project and it doesn’t seem to work.

    • Adam Craven says:

      I haven’t tried it in MVC 1, but I highly doubt that it would work in MVC 1 since it is specifically written to use the MVC 2 JavaScript.

  13. sickfish says:

    i encountered an interesting problem with this today. it seemed that all the script was running properly, but my client-side validation was still not working. turns out i had an existing form on the page in addition to the one being loaded dynamically with a click on an Ajax.ActionLink. … turns out neither of my forms had “id” attributes specified, and MVC was naming them both “form0″, so the code in MicrosoftMvcValidation.js was attempting to hook up the validation metadata for my ajax-loaded form to the other form. so, the moral of the story is: make sure to id your forms.

    • Adam Craven says:

      Yes, that is very good advice… always id your forms if using MVC validation and if you potentially may have multiple forms in the same DOM at some point in time (it is not a problem with my script however! :)

  14. turkish says:

    Fantastic! Works (at long last!)

  15. Alan Mosley says:

    Your script works fine as far as validation goes but for me it is not firing OnSuccess, am i missing somthing

    I have hokked OnSucess to reloadCustomers, but does not fire, is there a working example somewhere i can have a look at, or if your not too busy can you have a look at mine,

    Thanks very much, your saving us all a lot of bother.

    Create Customer

    Personal Details

    Address

    Busines Number and Name

    Category

    function CompleteFunction(ajaxContext) {
    $(“#form0″)[0].validationCallbacks = [];
    $clearHandlers($(“#form0″)[0]);
    var newDiv = document.createElement(“div”);
    $(newDiv).html(ajaxContext.get_data());
    var forms = newDiv.getElementsByTagName(“form”);
    $(“#form0″).html(forms[0].innerHTML);
    $(“#form0″)[0].action = forms[0].action;
    Sys.Mvc.FormContext._Application_Load();
    return false
    }

    function reloadCustomers() {

    try {
    loadCustomers()
    }
    catch (e) {
    alert(“error”)
    }
    return false
    }

    function reloadCustomersFailed() {

    }

    function abnCheck() {
    var abn = document.getElementById(‘abn’)
    if (abn.value) {
    var obj = { “abnNumber”: abn.value }
    $.post(“/JobAdmin/Customer/GetAbn”, obj, abnResults, “Json”);
    }
    }

    function abnResults(obj, value, exception)
    {
    obj = GetJsonObject(obj)
    var businessName = document.getElementById(‘businessName’)
    if (obj.IsCurrent && obj.IsActive)
    {
    businessName.value = obj.businessName
    }
    else
    {
    alert(“Number not valid”)
    var abn = document.getElementById(‘abn’)
    abn.value = ”
    businessName.value = ”

    }
    }

    • Adam Craven says:

      Hi Alan

      Sorry it took so long, but I just dug your comment out of the automatic spam filter…

      It looks like you are doing a lot of hard work in your CompleteFunction() – a lot of work that is done automatically for you if you use and specific the UpdateTarget in the AjaxOptions class.

      regards
      Adam

  16. Man – I wish I had found this earlier! Havn’t tried it yet but this was one thorn in my side when I downloaded MVC 2 back in March. Will try soon!

  17. Mike Paterson says:

    I was stoked to find this post but I ‘element’ is null:

    var scripts = $(“#” + element.id).find(“script”);

    <% using ( Ajax.BeginForm( "Add", new { controller = "Locations" }, new AjaxOptions { HttpMethod = "Post" }, new { id = "add-location-form" } ) )

  18. Toto says:

    Thank you very much. Your solution works like a charm.

    I just had to adapt it a bit so it also works with jQuery.ajax() (as some of my html is also loaded through it, by the jQuery.tabs plugin mostly).

    I changed this line :
    Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._globalEvalScriptInElementId(ajaxContext.get_updateTarget());

    To this line :
    Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._globalEvalScriptInElementId(document.getElementById(ajaxContext.get_updateTarget().id));

    And also this line :
    var scripts = jQuery(“#” + element.id).find(“script”);

    To this line, in your code :
    var scripts = jQuery(element).find(“script”);

    Otherwise I had to put this in my own code to bind your solution to jQuery :

    jQuery(document).ready(function () {
    // Binding jQuery to the AjaxLoadedContentScriptFix solution
    jQuery(document).ajaxSuccess(function (event, XMLHttpRequest, ajaxOptions) {
    // Only HTML code, no Json
    if (jQuery.isXMLDoc(XMLHttpRequest.responseText)) {
    // Make any dynamically loaded script execute
    Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._globalEvalScriptInElementId(XMLHttpRequest.responseText);

    // Reinitialise the MVC validation
    Sys.Mvc.MvcHelpers.AjaxLoadedContentScriptFix._reinitialiseMvcValidation();
    }
    });
    });

    Hope this will help other…

  19. Frank says:

    I completely agree with your way of thinking – extending the OnSuccess handler is the way to go. The last thing I want to do is to wrote OnSuccess=”refreshClientValidation” all the time.

    Too bad I get an error though. On line 67, “element is null”. The line:

    var scripts = $(“#” + element.id).find(“script”);

    I’ve added a simple if(element != null) within that function, makes the errors go away but the client-side validation is not fired at all.

    Help would be greatly appreciated.

  20. Frank says:

    Firefox 3.6.8.

    • Adam Craven says:

      Did you specify the UpdateTargetId in the AjaxOptions?

      • Frank says:

        No, I did not. It’s a BeginForm call which doesn’t have to place a result anywhere, so I didn’t consider it to be necessary.

        I’ve added it, just to see if it worked. Alas, it didn’t. The BeginForm call:

        using (Ajax.BeginForm(“RegistreerWerktijd”, “Home”, new AjaxOptions() { UpdateTargetId = “hello5″, HttpMethod = “Post”, OnBegin = “function() { registreerWerktijdBegin(‘ewr’); }”, OnSuccess = “function() { registreerWerktijdSuccess(‘ewr’); }” }))

    • Adam Craven says:

      And you have a <div id=”hello5″></div> somewhere in your DOM?

      I’m a little confused about the scenario you are presenting though… if you don’t have any HTML that is returned, then there is nothing that needs to be reinitialised.

      This script is to ensure that the return HTML that is injected into the UpdateTargetId DOM element will work with the MVC Validation and to execute any JavaScript that was returned as part of that HTML.

      • Frank says:

        Ah, of course. The BeginForm code I posted is from the partial view that’s loaded through AJAX into my page. Here is the code that executes this AJAX call:

        The DIV “dialogEindWerkRegistreren” is in my DOM and the form is loaded within that DIV perfectly. The client-side validation is the only thing that fails.

  21. Frank says:

    Posting the code once more, it seems HTML (ASP) tags are not allowed.

    Ajax.ActionLink(“eind werk registreren”, “EindWerkRegistreren”, new AjaxOptions() { UpdateTargetId = “dialogEindWerkRegistreren”, InsertionMode = InsertionMode.Replace, OnBegin = “showModalLoading”, OnSuccess = “function() { hideModalLoading(); displayDialog(‘#dialogEindWerkRegistreren’, ‘Eind van werk registreren’, 500, 350); }” })

    • Adam Craven says:

      Ok, that looks fine.

      On the server side, inside the view you are returning asynchronously, are you doing a “<% Html.EnableClientValidation()%>”?

      Can you verify that javascript for the mvc client-side validation is actually being returned and put into your div. (using fiddler or firebug perhaps)?

      • Frank says:

        I didn’t have it there. Put it there but unfortunately no success. There is JavaScript in the response, way down at the bottom is metadata for validating the form:

        if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; }
        window.mvcClientValidationMetadata.push({“Fields”:
        (and a couple of pages more)

        The form tag itself should trigger it as well:

        form action=”/Home/RegistreerWerktijd” id=”form0″ 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, httpMethod: 'Post', updateTargetId: 'hallo5', onBegin: Function.createDelegate(this, function() { registreerWerktijdBegin('ewr'); }), onSuccess: Function.createDelegate(this, function() { registreerWerktijdSuccess('ewr'); }) });”

        Odd!

      • Frank says:

        Alright, progress! I’ve been working on a testing environment where I reproduce the behavior my application is giving – I found the source of the trouble.

        In my application, I’m rending the same partial view in different contexts. Thus, the same fields are rendered in the DOM multiple times. This obviously causes errors because of the duplicate identifiers.

        For this, I used a solution described here: http://blog.stevensanderson.com/category/jquery/. This prefixes all my fields with a value I supply. I haven’t found the exact solution or the exact underlyting problem yet, but I can tell you that it has nothing to do with your script. In my testing environment, no problems occur whatsoever.

        I’ll keep you posted on my findings :-)

      • Frank says:

        Found it. I’m rendering one form normally and one through AJAX. Both receive the form identifier “form0″. This was the underlying problem for the validation not working properly.

        I’ve fixed this issue by manually setting the form ID HTML attribute, but now model binding fails… Sigh. Again, your code isn’t the problem anymore. Thanks :)

  22. bill says:

    Ok, I definitely like this method much better but just like the OnSuccess method, there are some compatibility issues with some UI controls such as the Telerik Grid. I get strange “Object required” JScript errors when using the Grid on my partial view. If I continue past the error, the validation still works.

    The error occurs on this line within MicrosoftMvcValidation.js

    formElement[Sys.Mvc.FormContext._formValidationTag] = this;

    In my situation I’m using a Telerik Grid and as soon as I remove it from my partial view, the error goes away. I read somewhere to surround a tag around the Telerik Grid and that seems to suppress the JScript error but causes other issues with the Grid.

  23. bill says:

    I was able to narrow it down further. It looks like the problem is here.

    Sys.Mvc.FormContext._Application_Load = function Sys_Mvc_FormContext$_Application_Load() {
    var allFormOptions = window.mvcClientValidationMetadata;
    if (allFormOptions) {
    while (allFormOptions.length > 0) {
    var thisFormOptions = allFormOptions.pop();
    if (thisFormOptions.FormId != ‘frmBargeLog1form’) {
    Sys.Mvc.FormContext._parseJsonOptions(thisFormOptions);
    }
    }
    }
    }

    allFormOptions contains 2 form collections. 1 for the Telerik Grid and the other for the partial view (the one that really needs the client side validation). As you can see I had to add a catch to prevent the Telerik form from being procssed.

    The only difference between the 2 form objects is that the ValidationSummaryId property does nto exist form the Telerik form object but exists on the partial view form object.

  24. Damian says:

    I’m having a similar problem than Bill… After applying your fix and after returning from an ActionLink (from PartialView to another PartialView) it usually comes up with the “Object required” error, and after fixing microsoftmvcvalidation.js directly to not process a null object I am getting a weird error when trying to click (sporadically) a button (the client validation keeps enabled I think) and the information is not submitted (not getting to the controller), it gets fixed once you click in another actionlink or if you F5…

  25. Damian says:

    Well… it looks that it gets fixed once you associate an HTML id tag for each HTML Form tag… maybe the validation framework was mixing up the validations between those forms. Just my two cents.

  26. Sean says:

    Good Stuff! just what we needed! thanks!

  27. hardywang says:

    I tried the solution, but ValidationSummary does not show up, any clue?

    Thanks!
    Hardy

  28. Jenda says:

    I have two problems with the script. First is seems the GlobalEvalScriptInElementId() gets called even if the AJAX returned a script (JavascriptResult) and in that case the element parameter is null.

    I added
    if (element == null) return;
    into the function.

    Second if there are several script tags in the html returned by AJAX, you join them to a single script that you then execute. If the snippets do not end with a semicolon, the parser sometimes gets confused and reports missing semicolons, syntax errors, unexpected variables, etc.

    I changed the
    allScriptText += scripts[i].text;
    to
    allScriptText += scripts[i].text + “;\n”;

    This seems to work fine for me.

    Thanks a lot for the script though!

  29. tomazeli says:

    dude…really good… it works!

  30. Any chance of repeating this awesome idea for the mvc3 jquery.unobtrusive scripts instead of Microsofts version?

    • Adam Craven says:

      Hi Russell, I’m sorry but no, I don’t have any plans for that.

      Best of luck!

  31. […] validation as much as possible was a requirement for me so did some googling and quickly found Adam Craven’s post about a solution to this: a library to extend Microsoft validation and Ajax to work as needed, […]

Follow

Get every new post delivered to your Inbox.

Join 78 other followers

%d bloggers like this: