Stop multiple form submissions

Hi,

Is there a YII specific method to stop multiple form submissions (Ex. User double clicking on submit button).

Thanks,

Chamal.

After you processed the form use $this->refresh() to reload the current page or $this->redirect() to load another page.

I think he was talking about after the user clicks submit but before the form is processed.

You change the action on the submit button so that when the user clicks it that it disables itself and then submits the form you’ll probably want to run your client side form validation first before disabling the button. As for a Yii specific method their isn’t one specifically that sets it up easily for you. However you can add to the beginform some html options to change the action of the form. something like


CHtml::beginform('yoururl', 'POST', array('onClick'=>'this.disabled=true;this.form.submit();');

I think it could be a better solution put the code below into main.php layout file




$cs=Yii::app()->clientScript;

$cs->registerScript('submit','

$(":submit").click(function() {

	$(this).attr("disabled",true);

})',CClientScript::POS_READY);



EDIT: This doesn’t work on IE (as usual…) so forget it

EDIT (again): This should work on IE too




$cs=Yii::app()->clientScript;

$cs->registerScript('submit','

$(":submit").mouseup(function() {

	$(this).attr("disabled",true);

	$(this).parents("form").submit();

})',CClientScript::POS_READY);



How I can send with POST request 'submit’s button value.

E.g. <input type=‘submit’ name=‘doAction’ value=‘Send’ />

in Controller I need to check:




if(!empty($_POST["MyModel"]) && isset($_POST["doAction"]))

{

    $model->attributes = $_POST["MyModel"];

}



But it doesn’t work, cos submit not exists in POST query.

Check the output of print_r($_POST). The submit button must appear. Why do you want to check it anyway? isset($_POST[‘MyModel’]) is sufficient to find out, wether your form was submitted.

I added a dynamic $formToken hidden field to every form then when form is submitted save the token to session. You can then check against this and take it from there…

This method works well, I can post code if needed :)

I liked makro’s solution best, thanks! I took it a bit further:

[list=3]

[*]Color the submit button gray when clicked

[*]Let the javascript event handler live in a file in the javascript directory, so that all of your javascript lives in one place, and does not live buried in php.

[*]Register the javascript client script in your main controller. I.e., rather than

cluttering the layout file, override the __construct() function for your default Controller class; so that your site as well as your modules can use it, and so that it works for all layouts.

[/list]

Here’s a working solution (except for Chrome!):




// 1. In your stylesheet, define the style "btn_gray":

btn_gray { 

    background-color: #efefef; 

}


// 2. Create the javascript file, "my-jquery.js", in the /javascript directory:

$(document).ready( function() {

   $(":submit").mouseup(function() {

      $(this).attr("disabled",true)

         .attr("class", "btn_gray")

         .parent().attr("class", "btn_gray")

         .parents("form").submit();

      });

})


// 3. Register the client script, in the constructor for your main Controller

public function __construct($id, CWebModule $module=null) {

   parent::__construct($id, $module);

   $baseUrl = Yii::app()->request->baseUrl;

   $clientScriptUrl = "{$baseUrl}/javascript/my-jquery.js";

   $clientScript = Yii::app()->clientScript;

   $clientScript->registerScriptFile($clientScriptUrl, CClientScript::POS_HEAD);

   // more construct code

}




This works on Firefox, MSIE. It fails on Chrome. If anyone knows why a registered client script might fail on Chrome, kindly update this post!

:mellow:

Hi Emily, I think the main problem is the mouseup option of your code. I would rather use another event, as it is going to be disabled after clicking, why not using ‘click’. Have you tried your code in Safari? I am sure the interpretation of mouseup leads to troubles too.

Try this





$(document).ready( function() {

   $(":submit").click(function() {

      $(this).attr("disabled",true)

         .attr("class", "btn_gray")

         .parent().attr("class", "btn_gray")

         .parents("form").submit();

      });

})




if also fails, do not use ‘parents()’, just use the normal way of submitting or grab the id of the form to do so.




this.form.submit();

$('#formid').submit();



Nite… 3 am here

Antonio, thanks SO much for replying at 3 a.m.!! (Or anytime.)

It turns out that the mouseup event is indeed the event to be handled; I just wasn’t doing the form submission correctly, as you suggested. This works now:







$(document).ready( function() {

   $(":submit").mouseup(function() {

      $(this).attr("disabled",true)

         .attr("class", "btn_gray")

         .parent().attr("class", "btn_gray");

      this.form.submit();

   });

})




'Tis, now, a thing of beauty.

Thanks, again, for your help!

:-*

Hey Antonio,

Actually, this solution works–exactly as you’ve written it–but it only works if my form consists of one or more inputs, plus a submit button.

IF, however, I create a CActiveForm widget, and only give it a single button, and no inputs, my controller ends up with an empty $_POST. I find this of course by inserting logging into my controller:




if ( empty($_POST))   {

   Yii::log("POST IS EMPTY!");

}



Any ideas?

Thanks so much…

Emily

Okay, I used a slightly different approach, and now my $_POST array is never empty.

The clue you gave me, Antonio, that allowed me to figure this out, is that the submit button is being disabled. In the event handler below, we disable the button, color it gray, and then re-enable it.





$(document).ready( function() {

   $("form").submit(function(event) {

      var form = event.currentTarget;

      // Disable the button, turn the background gray, and set the text to "Working..."

      $("#" + form.id).find("input[type=submit]").attr("disabled",true)

         .attr("class", "btn_gray")

	 .attr("value", "Working...");


      // Submit

      form.submit();


      // Re-enable the button

      $("#" + form.id).find("input[type=submit]").attr("disabled",false);


      return true;

   });

})




Works in FF, MSIE, and Chrome.

Thanks again Antonio and others!

Emily

:)

Great you found the solution Emily,

Nevertheless, about your question on the empty Form, weird to drop an empty form but you could easily avoid form submission if form.elements.length are just the total number of buttons.

Anyway, I am wondering why you re-enable your submission button after you have submitted the form? I never done that after submission, only on the AJAX cases, wher e I need to handle buttons state. Any reasons for that?

Thanks in advance

Hi Antonio,

I found that disabling button is indeed problematic. So a simpler approach is to unbind the submit button from "click"; change the text to "Working…"; and change the color to gray.

So, here’s the final solution, which works on FF, MSIE, and Chrome:




// 1. In your stylesheet, define the style "btn_gray":

btn_gray { 

    background-color: #efefef; 

}


// 2. Create the javascript file, "my-jquery.js", in the /javascript directory:

$(document).ready( function() {

   $(":submit").click(function() {

  // Only recolor the button if it's inside a "btn btn_green" div

   var containingDivClass = $(this).parent().attr("class");

   if (containingDivClass == 'btn btn_green')   {

      $(this).unbind("click").attr("value", "Working...").parent().attr("class", "btn btn_gray");

      this.form.submit();

      return false;  // must do this!!

   }

});


// 3. Register the client script, in the constructor for your main Controller

public function __construct($id, CWebModule $module=null) {

   parent::__construct($id, $module);

   $baseUrl = Yii::app()->request->baseUrl;

   $clientScriptUrl = "{$baseUrl}/javascript/my-jquery.js";

   $clientScript = Yii::app()->clientScript;

   $clientScript->registerScriptFile($clientScriptUrl, CClientScript::POS_HEAD);

   // more construct code

}


// 4. Use it, in a view. Explicitly name the button 'doit_button'

echo CHtml::submitButton('Doit', array('name'=>'doit_button'));


// 5. Check explicitly to see if form was submitted (this addresses

//    iGrog's question about the $_POST array, above)

if (isset($_POST['doit_button'])  )

    // do stuff

}




:mellow:

I’ve noticed one gotcha with the javascript event handler as written:

If a page contains more than one form, even though only one form got submitted, all of the buttons get disabled, turned gray, etc. A usability issue. Ideas anyone?

use jquery closest to check which form was submitted

Time to wrap this up. My question here isn’t a Yii one, but a jQuery one. A working solution is on the jQuery forum, here:

http://forum.jquery.com/topic/how-to-disable-submit-button-after-form-submission

Thanks Antonio and Gustavo!

:)

Im glad i could help

Hi, I came up with this solution for AJAX validated forms.

You just have to pass it the rigth selector for the submit button.

place this piece of code at the end of your _form.php partial views (and properly modify the button css selector).




<?php

$no_dbl_click=<<<EOD


   $("the_button").click(function(event) {      

   //extra_js;

   var target= $(event.currentTarget);

  // console.dir(target);

    if(!target.attr('disabled')){

    target.attr('rel',target.val());  

    }      

    target.attr("disabled",true)

         .attr("class", "btn_gray")

         .attr("value", "Un moment...");

      var the_expr="$('#" + target.attr('id') + "')";

      var the_statement=the_expr + '.attr("disabled",false).val(' + the_expr + '.attr("rel"))';      

      setTimeout(the_statement,2000);

     target.parents('form').submit(); 

     return true;

   });


EOD;

$js=str_replace('the_button','#form_submit',$no_dbl_click);

Yii::app()->clientScript->registerScript($this->id.'no_dbl_click',$js);

?>



Most important, this works with ajax and clientside validation, so the user may try again and again without reloading the page form. 2 secods is enough for the succesful post to redirect, and should be enough for validation messages to appear.

This code will disable the button for 2 seconds regardless if ajax/clientside form validation fails or succeeds.

It then attemps a submit on the parent form, which is the Yii generated form.