Wednesday, 21 January 2009

ASP.NET - Disabling the submit button to prevent double submissions

If a user clicks on a form submit button and the page response is slow, the user may often re-click that button. Because the original request has already been submitted, you may find that the user has made a double submission. In e-commerce sites, the double submission of a credit card payment may be very unpopular with your customers.

This is a common problem faced by web developers, and there are a variety of ways to try to prevent it. However, I have not seen a really elegant way to solve the problem, so I set about trying to find a simple and effective solution.

There were some key issues I wanted to address:
  1. I wanted to disable the button when it was clicked, but only if the page was valid
  2. I did not want to manually add code to every button in my application
  3. I did not want to break the existing validation, especially when using validation groups
  4. I wanted to preserve the CausesValidation property
To achieve these goals, I set about creating a custom button that inherited the standard ASP Button. This custom button would replace the existing buttons in my application.

First add a new class to your App_Code directory called "EnhancedButton" and then override the OnPreRender event:

namespace Junto.WebControls
{
/// 
/// If CausesValidation then check the current ValidationGroup is valid
/// and if so, disable the button.
/// 
[ToolboxData("<{0}:EnhancedButton runat=server>")]
public class EnhancedButton : Button
{
protected override void OnPreRender(EventArgs e)
{
if (this.CausesValidation)
{
StringBuilder sb = new StringBuilder();
sb.Append("if (typeof(Page_ClientValidate) == 'function') { ");
sb.Append("if (Page_ClientValidate('" + this.ValidationGroup + "') == false) { return false; }} ");
sb.Append("this.value = 'Please wait...';");
sb.Append("this.disabled = true; ");
sb.Append(this.Page.GetPostBackEventReference(this));
sb.Append(";");
this.Attributes.Add("onclick", sb.ToString());
}

base.OnPreRender(e);
}
}
}

Now, we add the following to our web.config to take advantage of the tagMapping feature:

<pages>
<controls>
<add tagPrefix="Junto" namespace="Junto.WebControls" />
</controls>
<tagMapping>
<add tagType="System.Web.UI.WebControls.Button" mappedTagType="Junto.WebControls.EnhancedButton" />
</tagMapping>
</pages>

The result is that all instances of standard Buttons are replaced with our new Enhanced custom button. You have no need to add extra code in every Page_Load. The Tag Mapping takes care of replacing the standard button across the web application.

And VoilĂ , our job is done!

2 comments:

  1. Angel Blandon11 March 2013 21:02

    Hi, i was trying to use this class to prevent double click but when is asigned to a button thats is targing a modalpopup the modal does not show, do you have any idea of why?

    ReplyDelete
  2. Offhand I have no idea without seeing some of your code. However, since my code is essentially adding an onclick JavaScript event to a button, I would hazard a guess that any onclick handler you have added to open your modal is being overwritten. To solve this, you might need to customize the way in which the JavaScript above is written, in order to make sure that your onclick event is not broken.

    ReplyDelete