I have a simple form that I don't want accidentally submitted multiple times.
I can easily block the user from multiple clicks when they first see the page, but I have no control over them hitting the back button.
So, having worked with Struts, I decided that a form submit token was the way to go.
Is there an easier way? Is this functionality already in Seam? If there isn't, how should I go about building this functionality into Seam?
// EDIT // Just a clarification here, I do NOT need something that will keep the user from double- clicking. That is already solved.
The specific use-case is as follows: The user clicks the button. Action runs. Some unspecified time in the future, the user hits the back button enough times to get back to the page with the button. The user clicks the button again.
How do I protect against that?
Seam comes with the s:token component and it is what you are looking for: http://docs.jboss.org/seam/2.2.0.GA/reference/en-US/html/controls.html#d0e28933
To avoid double submits caused by impatiently pressing the submit button you could use a piece of Javascript which disables the submit button a few ms after onclick.
Example:
<h:commandButton
id="foo"
value="submit"
action="#{bean.submit}"
onclick="setTimeout('document.getElementById(\'' + this.id + '\').disabled=true;', 50);"
/>
To avoid double submits by pressing the back button and ignoring the browser's warning that you may risk to resend the data, you need to implement the Post-Redirect-Get (PRG) pattern.
In JSF this can be done in basically 2 ways. Either using <redirect/>
in <navigation-case>
:
<navigation-case>
<from-action>#{bean.submit}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/page.jsf</to-view-id>
<redirect/>
</navigation-case>
or by invoking ExternalContext#redirect()
in bean's action method:
public void submit() {
doYourThing();
FacesContext.getCurrentInstance().getExternalContext().redirect("page.jsf");
}
The only disadvantage is that the redirect implicitly creates a new request, hereby garbaging the initial request including all of its attributes (and thus also all request scoped managed beans and FacesMessages
). In some cases it doesn't matter, but in other cases it surely would. I don't do Seam, but if I am correct, they have solved this problem with help of the so-called conversation scope and automatically retaining the FacesMessages
through the redirect. You could take benefit of it.
With a4j/RichFaces, use the a4j:queue or assign a queue to the button. That way multiple clicks will get queued and only one will actually go through. The way to set it globally for your app if you're using RichFaces (we did this instead of setting up a queue everywhere), is to put the following in your web.xml:
<context-param>
<param-name>org.richfaces.queue.global.enabled</param-name>
<param-value>true</param-value>
</context-param>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With