views:

2515

answers:

3

I have a form containing a list of input fields that the user populates with prices (numbers). I have set their css classes to "required" and "number" so that the jQuery validation plugin validates them accordingly. All that is working perfectly well. The errors are displayed when each field is either not a number or the value is missing etc.

This entire form is actually inside a nyroModal (jQuery plugin) model popup window. But that shouldn't matter, its just an .aspx web form regardless. Within that, I have also used an UpdatePanel and put everything inside it, because my form is essentially a 2 stage process. There are some basic steps the user must complete on the first 'page' of the form. When that validates and works, I just hide/show some elements within the UpdatePanel to show the user the next 'page' of the form, which is all the price textbox input fields as described above.

I am using an ImageButton as the trigger point for validating the form. By default, if you just leave an ImageButton the way it is, .Net will fire off a postback, AJAX postback in my case, because its all happening inside an UpdatePanel. Which again, is desired. I don't want an ugly full postback for obvious reasons.

After lots of reading, I needed a way to attach to .Net's PageRequestManager in javascript so that I could intercept when .Net wanted to do a postback, based on if jQuery had successfully validated the form or not. So my logic says: By default, do not allow postbacks, but when jQuery successfully validates the form, set a variable which says that postbacks are now allowed, then the next time a postback tries to happen, it will go through. And therefore display success to the user.

This is kind of all working reasonably well, but there is one final problem.

The jQuery validation plugin's validate({success}) option seems to be wrongly firing after validation is successful on each individual field, not the entire form. I.e. if I enter a number into any one of the price input fields, this success option will fire and set my cancelPostback variable to false, which in turn means that the form now submits even though it hasn't been validated, just because one field was valid. I would have expected this validate({success}) option to only fire once the entire form was validated.

Have I interpreted this incorrectly, and in fact its doing what its meant to be? If so, how can I simply set my cancelPostback variable to true ONLY when the ENTIRE FORM is validated?

Thanks heaps for any insight or heads up.

My jQuery code to implement the validation and also intercept the .Net postback event is as follows:

<script type="text/javascript">
    // By default, don't allow Postback until we've successfully validated our form on the client-side.
    var cancelPostback=true;

    // Every single time the page loads, including every single AJAX partial postback
    function pageLoad()
    {
        // Reset the value to say that "We don't allow Postbacks at this stage"..
        cancelPostback=true;

        // Attach client-side validation to the main form
        $("#<%= form1.ClientID %>").validate({ success: function() { alert('validation succeeded!'); cancelPostback = false; } });

        // Grab a reference to the Page Request Manager
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_initializeRequest(onEachRequest);

        alert("Page Load");
    }

    // Our own custom method to be attached to each new/future page request..
    function onEachRequest(sender, args)
    {
        alert("About to cancel? " + cancelPostback);
        // Check to see if we need to Cancel this Postback based on Conditional Logic within this page..
        args.set_cancel(cancelPostback);
    }
</script>
+1  A: 

I would suggest looking at the submitHandler and invalidHandler options. One or the other ought to work for what you want. The best way, I think, would be to allow postbacks by default and set the cancelPostback to true in the invalidHandler. Alternatively, you could write your own submitHandler that calls __doPostBack directly and replaces the submission via the ImageButton.

tvanfosson
A: 

Thanks heaps tvanfosson for the prompt assistance.

Ok update:

I followed more and more links and eventually found the documentation for "success". The documentation on the plugin's own home page is terrible and omits many key features :) But on the official jQuery page its "better". The "success" option is meant to fire every single time an individual field is successfully validated. So that was working by design and I was wrongly assuming it would only fire when the entire form was valid.

I tried your invalidHandler idea. I loved this idea. One issue I came up against was that the ImageButton's postback would still ALWAYS fire off first, so it would start posting back before the invalidHandler code would run. So the postback would still go ahead, then after that, invalidHandler would disable future postbacks, they were firing in the wrong order unfortunately. But apart from that, it showed every other sign of being the perfect solution.

I then went the other way trying the submitHandler way, i.e. the inverse. Again, this suffered from some order problems, because the postback would again fire first, but be blocked, then submitHandler would allow future postbacks, but it was already too late, so this postback was missed. You'd have to click the button again twice to get it to go.

So the final semi-kludgey solution, that's not too bad to be honest, was:

I changed this line to: // Attach client-side validation to the main form $("#<%= form1.ClientID %>").validate({ submitHandler: function() { PreventPostback = false; __doPostBack('UpdatePanel1', ''); } });

Which as you can see, first toggles the PreventPostback variable, but then fires off a new postback manually, because the first postback would have already been blocked and died off. So this manual __doPostBack call ensures that a new postback gets fired off respecting the new value of PreventPostback.

I removed the event methods from behind the ImageButtons, and made a new method in the server side code called "DoPostBackLogic()" which is only called when the page is posted back, which I know must have been from this validation logic, because that's the only postback that can happen on my form. Then all of the hide/show/save logic happens in there for me. Which means when I am manually calling the __doPostBack, I can simply use the ID of the UpdatePanel and not worry about mimicking a particular ImageButton etc.

This way it works perfectly. Postbacks are disabled by default, and when the form is valid, Postbacks are then allowed, but a postback is then manually triggered so that the new value takes effect.

So this is essentially your second solution, thanks once again for listening and sharing! :)

Aaron
A: 

I had problems with your solution because all browsers except for IE were firing the "onEachRequest" function BEFORE the jQuery validator's submitHandler() function. I came up with a more elegant solution that simply cancels the postback if validation fails, rather than relying on a preset variable.

  Sys.Application.add_init(function() {
    var prm = Sys.WebForms.PageRequestManager.getInstance();
    prm.add_initializeRequest(onEachRequest);
  }); 

  function onEachRequest(sender, args) {
    if (args.get_postBackElement().id == <%= [YourSubmitButton].ClientID %>) {
      args.set_cancel(!$('#aspnetForm').valid());
    }
  }
Jacob Tabak

related questions