How to get unobtrusive remote jquery validator to execute async ..?

In an MVC3 application using unobtrusive jquery validation and view / model using the [Remote] validator: I try to disable the submit button and display a wait icon during remote validation and when a valid form is submitted to the server. I thought I had it nailed until I tried it in IE8.

The problem was that GC and FF did not fire the form submit event when the form was invalid, so I just turned off the submit button during this event. However, IE8 fires this event when the form is invalid, as a result of which the user will never be able to click it again. (IE8 does not submit the form, but the event fires.)

I tried to bind the function to the submit button click event. There I turned off the submit button, showed a wait icon and had the following:

$('[data-app-form-submit-button="true"]').live('click', function (e) { var form = $(this).parents('form'); var icon = form.find('[data-app-form-submitting-icon="true"]'); icon.show(); $(this).attr('disabled', 'disabled'); $.ajaxSetup({ async: false }); var isValid = form.valid(); $.ajaxSetup({ async: true }); alert(isValid); }); 

The problem is that the ajax configuration call does not disable asynchronous mode. This happens if I get it out of the click function, but then it disables asynchronous tuning for everything. Instead, the page immediately reports β€œtrue”, verified by setting a breakpoint in the remote validation action method.

Any ideas?

Additional Note:

I forgot to mention that in IE8, a send event is fired only when the text field in question cannot perform validation, which can happen on the client. For example, if this fails or regex, send (). For a remote validation action method, it does not start. However, as soon as it fails to validate the client, subsequent remote checks also fire the IE8 send event.

Reply to Russ Cam (Comment # 1)

Here is the relevant code in the viewmodel:

 public class SignUpForm : IValidatableObject { [DataType(DataType.EmailAddress)] [Display(Name = "Email Address")] [Required(ErrorMessage = "Email Address is required.")] [RegularExpression(@"^(email regex here)$", ErrorMessage = "This is not a valid email address.")] [Remote("Validate", "ControllerName", "AreaName", HttpMethod = "POST")] public string EmailAddress { get; set; } public IEnumerable<ValidationResult> Validate( ValidationContext validationContext) { 

I'm glad you made me take a look at the <form> rendering. The form tag and input elements are as follows:

 <form action="/post-action-method" method="post" novalidate="novalidate"> ... <input class="text-box single-line" data-app-focus="true" data-val="true" data-val-regex="This is not a valid email address." data-val-regex-pattern="^(email regex here)$" data-val-remote="&amp;#39;Email Address&amp;#39; is invalid." data-val-remote-additionalfields="*.EmailAddress" data-val-remote-type="POST" data-val-remote-url="/validate-action-method" data-val-required="Email Address is required." id="EmailAddress" name="EmailAddress" type="text" value=""> ... <input type="submit" value="Submit this form" data-app-form-submit-button="true" /> 

I still have not seen the attribute novalidate = "novalidate". Here's what it looks like in the cshtml file:

 @using (Html.BeginForm()) { @Html.EditorForModel() @Html.AntiForgeryToken("assault") } 

I also use an anti-fake token, if that matters. Thanks.

+7
source share
4 answers

These are hackers, but I suggest trying the following.

First, hide the submit button with display="none" and show your own submit button, which launches your script above.

Secondly, on your page add the flag var [ var remotePending = false; ] and a variable to call setInterval [ var intervalPending; ], and in your script set the flag to true, then call the following with intervalPending = setInterval('remoteCheck()', 200);

 function remoteCheck() { if ($.validator.pendingRequest == 0) { // requests are done // clear interval clearInterval(intervalPending); // re-enable our "submit" button // "click" the hidden button $("#hiddenSubmit").click(); } // we will try again after the interval passes } 

This will allow you to wait for the pending remote check to complete, and then continue. I have not tested, so you may have to play to get it to work the way you want.

+3
source

I was a bit late on the table with this, but I came across this question trying to solve a similar problem. I also did not want to use Interval, so I prepared another solution and thought it might be useful for others.

In the end, I decided to override the startQequest jQuery methods and stopRequest methods to add my own logic. After doing my own logic, I just call the old validation methods.

 // Extend jQuery validator to show loading gif when making remote requests var oldStartRequest = $.validator.prototype.startRequest; $.validator.prototype.startRequest = function (element) { var container = $('form').find("[data-valmsg-for='" + element.name + "']"); container.addClass('loading'); oldStartRequest.apply(this, arguments); }; var oldStopRequest = $.validator.prototype.stopRequest; $.validator.prototype.stopRequest = function (element) { var container = $('form').find("[data-valmsg-for='" + element.name + "']"); container.removeClass('loading'); oldStopRequest.apply(this, arguments); }; 

All I wanted to do for my purposes was to simply add the β€œload” css class to the next check box to show the download gid, but you can easily expand it to enable / disable the button, etc.

+2
source

I found myself puzzled last night, and having tried various approaches, I ended up evolving the tips. Since advsellorbens did not work right out of the box, I thought I would share it so that others would not have the same headache. This is not as β€œclean” as I would like (I really hate using intervals). But that seems sufficient.

 var iIntervalId = null; // // DECLARE FUNCTION EXPRESSIONS // //============================================================================== // function that triggers update when remote validation completes successfully //============================================================================== var fnPendingValidationComplete = function () { var validator = $("#frmProductType").data("validator"); if (validator.pendingRequest === 0) { clearInterval(iIntervalId); //Force validation to present to user (this will not retrigger remote validation) if ($("#frmProductType").valid()) { alert("valid - you can submit now if you like"); } //else { alert("invalid"); } } //else { alert("NOT YET"); } }, //============================================================================== // Trigger validation //============================================================================== fnTriggerValidation = function (evt) { //Remove any cached values to ensure that remote validation is retriggered $("#txtProductType").removeData("previousValue"); //Trigger validation $("#frmProductType").valid(); //Setup interval which will evaluate validation (this approach because of remote validation) iIntervalId = setInterval(fnPendingValidationComplete, 50); }; fnTriggerValidation(); 
+1
source

I decided to solve this, but the disable / wait image show button only happened when there was a valid form submitted. This is all right, since multiple invalid form submissions are idempotent, the user can click as many as they want. (In addition, the remote validation action method is cached relative to its parameter values.)

 $(function () { $('form').bind('invalid-form.validate', function () { $('form').each(function () { $(this).find( '[data-app-form-submitting-icon="true"]').hide(); var button = $(this).find( 'input[type="submit"][data-app-form-submit-button="true"]'); setTimeout(function () { button.removeAttr('disabled'); }, 1); }); }); $('form').live('submit', function (e) { $(this).find('[data-app-form-submitting-icon="true"]').show(); var button = $(this).find( 'input[type="submit"][data-app-form-submit-button="true"]'); setTimeout(function () { button.attr('disabled', 'disabled'); }, 0); }); }); 

This is a bit hacked too, but when I did not use setTimeout, the submit action never launched an invalid handler.

0
source

All Articles