Sys.Mvc.FormContext.OnSuccessEnableClientValidation = function (ajaxContext) {
//Getting the update target container
var updateTarget = document.getElementById(ajaxContext.$4.id);
//Getting all script elements in it (script elements injected with innerHtml are not executed)
var mvcClientValidationMetadataOldScripts = updateTarget.getElementsByTagName('script');
var mvcClientValidationMetadataNewScripts = [];
//For every script element
while (mvcClientValidationMetadataOldScripts.length > 0) {
//Create a new one
var mvcClientValidationMetadataNewScript = document.createElement('script');
mvcClientValidationMetadataNewScript.type = 'text/javascript';
mvcClientValidationMetadataNewScript.text = mvcClientValidationMetadataOldScripts[0].text;
//Add it to collection
mvcClientValidationMetadataNewScripts.push(mvcClientValidationMetadataNewScript);
//And remove old one
updateTarget.removeChild(mvcClientValidationMetadataOldScripts[0]);
}
//For every new script element
while (mvcClientValidationMetadataNewScripts.length > 0) {
//Append it to update target container, this way they will be executed and generate needed metadata
updateTarget.appendChild(mvcClientValidationMetadataNewScripts.pop());
}
//Calling Microsoft validation initialization for new metadata
Sys.Mvc.FormContext._Application_Load();
}
Now we can use our new function as OnSuccess handler for Ajax.BeginForm:
<% Html.EnableClientValidation(); %>
<% using (Ajax.BeginForm("...", "...", null, new AjaxOptions() { UpdateTargetId = "...", OnSuccess = "Sys.Mvc.FormContext.OnSuccessEnableClientValidation" }, new { id = "..." })) { %>
...
<% } %>
Now it should work. Let's do the same for MicrosoftMvcJQueryValidation.js. We will add the following function inside of it:
function __MVC_OnSuccessEnableClientValidation(ajaxContext) {
//Getting the update target container
var updateTarget = document.getElementById(ajaxContext.$4.id);
//Getting all script elements in it (script elements injected with innerHtml are not executed)
var mvcClientValidationMetadataOldScripts = updateTarget.getElementsByTagName('script');
var mvcClientValidationMetadataNewScripts = [];
//For every script element
while (mvcClientValidationMetadataOldScripts.length > 0) {
//Create a new one
var mvcClientValidationMetadataNewScript = document.createElement('script');
mvcClientValidationMetadataNewScript.type = 'text/javascript';
mvcClientValidationMetadataNewScript.text = mvcClientValidationMetadataOldScripts[0].text;
//Add it to collection
mvcClientValidationMetadataNewScripts.push(mvcClientValidationMetadataNewScript);
//And remove old one
updateTarget.removeChild(mvcClientValidationMetadataOldScripts[0]);
}
//For every new script element
while (mvcClientValidationMetadataNewScripts.length > 0) {
//Append it to update target container, this way they will be executed and generate needed metadata
updateTarget.appendChild(mvcClientValidationMetadataNewScripts.pop());
}
//Getting new metadata
var allFormOptions = window.mvcClientValidationMetadata;
if (allFormOptions) {
//For every form in metadata
while (allFormOptions.length > 0) {
//Enable validation for form based on metadata
var thisFormOptions = allFormOptions.pop();
__MVC_EnableClientValidation(thisFormOptions);
}
}
}
We can use this function in the same way as previous one:
<% Html.EnableClientValidation(); %>
<% using (Ajax.BeginForm("...", "...", null, new AjaxOptions() { UpdateTargetId = "...", OnSuccess = "__MVC_OnSuccessEnableClientValidation" }, new { id = "..." })) { %>
...
<% } %>
I can't say that this solution has been tested in every possible scenario, so if you find any problems with it, just let me know.
Email
16 comments:
Thanks for this information on the client-side validation, it helped me come up with my solution to the issue: http://adammcraventech.wordpress.com/2010/06/11/asp-net-mvc2-ajax-executing-dynamically-loaded-javascript/
Hi. I actually tried the solution posted on the link by "adammcraven".
http://adammcraventech.wordpress.com/2010/06/11/asp-net-mvc2-ajax-executing-dynamically-loaded-javascript/
The javascript here parses the script tags and evaluates them by default on "OnSuccess" event of AJAX call. But it doesn't seem to work.
Is it possible that your function be modified to call itself on OnSuccess event of every Ajax.ActionLink call.
Hi. I'm currently on vacation, so I'm offline most of the time. I will try to modify my function the way you asked and respond as soon as possible.
i was using
$.load to add elements to my form instead of Ajax.BeginForm
I was able to adapt your solution for MicrosoftMvcValidation.js with a small change
var updateTarget = document.getElementById(ajaxContext.$4.id);
to
var updateTarget = document.getElementById($(this).attr('id'));
This solution is excellent, but when the submit button is clicked, the data is being validated both client and server side when the client side validation should be preventing the form from posting.
if you stick a breakpoint on the post action and enter invalid data, you'll see what I mean.
Any suggestions on how to resolve this?
I would just use Jquery validation, but I'm really set on using Data Annotation Validators. I just wish MS would document their MvcAjax and MvcValidation files so I could have some idea of what's going on in there.
To be honest, that wouldn't be the best idea. You can't rely only on client-side validation, because it can be easly tempered with. You should always revalidate your data on server.
hmmm I need to be more clear in how I ask this...
When using the HTML.BeginForm() helper, if the client-side validation doesn't pass, the form doesn't post. This is expected behavior. Why run server code if we know data is invalid?
When using the AJAX.BeginForm() helper, the form posts whether the Client catches any errors or not. How can we prevent the client from posting if the client catches errors?
If this is still unclear, I'll whip up an example.
Now I understand what you are asking, but I would rather see it as some kind of JavaScript bug. I will test this case as soon as possible and let you know.
Right, it's definitely a javascript issue. The html and ajax helpers are just wired up to behave very differently and Microsoft doesn't seem to have been very mindful of ajax applications when they designed their client validation helper.
This whole thing has me considering writing my own client validation library.
Thanks in advance for the help! I've been beating my head against the wall for a few days on this.
Ryan, I have tested the case you have described, and if the solution described in this psot is used, then there is no problem with client side validation. The form doesn't get POST if it's not valid. Please check if you have implemented aboive solution properly. If you will still have problems, plz contact me on email with peace of your code so I can test that.
It looks like this was a bug in an older version of MVC. We're still using VS2008 at my work-place. I went home and tried to duplicate the issue in 2010 and everything worked as it should.
I also found out that the code you have in this post can be replaced with:
function InitValidation() {
Sys.Application.remove_load(arguments.callee);
Sys.Mvc.FormContext._Application_Load();
}
Thanks again for the help, and great blog you have here!
Hi,
I tried your solution, but somehow ValidationSummary and ValidationMesssge does not seem to show up.
Any idea?
Thanks
Hardy
Hi Hardy,
I would need more information about yours scenario so I can try to reproduce it.
Greetings
This is a test.
I seem to be getting the same issue in MVC 4 RC, although I am using the jquery.validate.min.js and /jquery.validate.unobtrusive.min.js scripts.
Would I still need to add the additional functions you have described here? or should these issues be resolved in the newer version of MVC?
Thanks.
The validation framework has changed a little bit since MVC 2. The issue should not be present anymore and if it`s appearing then the reason is most probably different and above solution will not resolve it I'm afraid.
Post a Comment