There is wide known scenario when click to APEX command button on custom VF page would take quite long time to perform calculation on server side and return the result back to UI. User may try to click the button again which will result in unexpected and undesired consequences. Especially if click to the button invokes DML operations.
There are a few workarounds to avoid such scenarios. In this post you will the most popular workarounds with analysis about their PROS and CONS.
Notes related to all examples below:
- As a $Resource.statusbar you can use any .gif animated "in progress" image
- In all demos we used external controller to build mathematic curves to simulate server activity
#1 Disabling of APEX command button using jQuery. Replacing button with animated GIF
Disabling the button once the user clicks the button could be the straight forward solution. In this solution we will replace the button once the user clicks the button using jQuery
VF page: DisableApexCommandButton to demo disable Button
VF page: DisableApexCommandButton to demo disable Button
<!--
Version : 1.0
Company : WebSolo Inc.
Date : 03.2016
Description : VF page "DisableApexCommandButton" to demo disable Button
History :
-->
<apex:page sidebar="false" showheader="false" cache="false" expires="0" controller="DisableApexCommandButtonContr">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<apex:composition template="{!$Site.Template}">
<apex:define name="body">
<apex:form style="width:550px;" id="f1">
<apex:outputPanel >
<apex:commandButton style="margin-top:20px;margin-left:250px;margin-bottom:25px;height:30px;font-size:12px" styleClass="b1" value="Generate" status="status" action="{!run}" rerender="f1"/>
<div style="display: none; margin-left: 250px;" class="statusbardiv">
<apex:image width="70px" url="{!$Resource.statusbar}" />
</div>
<apex:selectRadio value="{!chart}">
<apex:selectOptions value="{!items}"/>
</apex:selectRadio><p/>
<apex:actionStatus id="status" onstart="$('.statusbardiv').css('display','block');$('.b1').css('display','none');" onstop="$('.statusbardiv').css('display','none');$('.b1').css('display','block');" />
<div>
<apex:chart height="300" width="500" data="{!data}">
<apex:legend position="top"/>
<apex:axis title="Y" type="Numeric" position="left" grid="true" fields="valY"/>
<apex:axis title="X" type="Numeric" position="bottom" fields="valX" />
<apex:lineSeries title="{!chart}" axis="left" xField="valX" yField="valY"/>
</apex:chart>
</div>
</apex:outputPanel>
</apex:form>
</apex:define>
</apex:composition>
</apex:page>
PROS: Easy to implement. Looks good.
CONS: Requires jQuery library.
#2 Call custom div as an overhead "modal window"
Another solution would be not try to "disable" buttons, but to call custom modal window which will prevent user from multiple clicks and multiple form submission.
VF page: DisableApexCommandButton
VF page: DisableApexCommandButton
<!--
Version : 1.0
Company : Websolo inc.
Date : 03/2015
Description : Call custom div as an overhead "modal window"
Update History :
-->
<apex:page sidebar="false" showheader="false" cache="false" expires="0" controller="DisableApexCommandButtonContr">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<style>
.disabledbutton {
pointer-events: none;
opacity: 0.4;
background-color:#E1E1E1;
}
</style>
<apex:composition template="{!$Site.Template}">
<apex:define name="body">
<apex:form style="width:550px;" id="f1">
<apex:actionStatus id="status" onstart="$('#mydiv').addClass('disabledbutton');$('.statusbardiv').css('display','block');" onstop="$('#mydiv').removeclass('disabledbutton');$('.statusbardiv').css('display','none');" />
<div style="display: none; position: fixed; z-index: 999; margin-left: 250px; margin-top: 200px" class="statusbardiv">
<apex:image width="70px" url="{!$Resource.statusbar2}" />
</div>
<div id="mydiv">
<apex:outputPanel >
<apex:commandButton style="margin-top:20px;margin-left:250px;margin-bottom:25px;height:30px;font-size:12px" styleClass="b1" value="Generate" status="status" action="{!run}" rerender="f1"/>
<apex:selectRadio value="{!chart}">
<apex:selectOptions value="{!items}"/>
</apex:selectRadio><p/>
<div>
<apex:chart height="300" width="500" data="{!data}">
<apex:legend position="top"/>
<apex:axis title="Y" type="Numeric" position="left" grid="true" fields="valY"/>
<apex:axis title="X" type="Numeric" position="bottom" fields="valX" />
<apex:lineSeries title="{!chart}" axis="left" xField="valX" yField="valY"/>
</apex:chart>
</div>
</apex:outputPanel>
</div>
</apex:form>
</apex:define>
</apex:composition>
</apex:page>
PROS: Works better in some cases
CONS: Not visually perfect for some users who not used to such approach. Also requires jQuery library.
#3 Disabling of APEX command button using apex:facet method
In this solution we will replace the button with "Processing..." text once the user clicks the button
VF page: DisableApexCommandButton
VF page: DisableApexCommandButton
<!--
Version : 1.0
Company : Websolo inc.
Date : 03/2015
Description : Disabling of APEX command button using apex:facet method
Update History :
-->
<apex:page sidebar="false" showheader="false" cache="false" expires="0" controller="DisableApexCommandButtonContr">
<apex:composition template="{!$Site.Template}">
<apex:define name="body">
<apex:form style="width:550px;" id="f1">
<apex:outputPanel >
<apex:actionStatus id="status">
<apex:facet name="stop">
<apex:commandButton style="margin-top:20px;margin-left:250px;margin-bottom:25px;height:30px;font-size:12px;width:90px" action="{!run}" status="status" value="Generate" disabled="false" rerender="f1"/>
</apex:facet>
<apex:facet name="start">
<apex:commandButton style="margin-top:20px;margin-left:250px;margin-bottom:25px;height:30px;font-size:12px;width:90px" action="{!run}" status="status" value="Processing..." disabled="true"/>
</apex:facet>
</apex:actionStatus>
<apex:selectRadio value="{!chart}">
<apex:selectOptions value="{!items}"/>
</apex:selectRadio><p/>
<div>
<apex:chart height="300" width="500" data="{!data}">
<apex:legend position="top"/>
<apex:axis title="Y" type="Numeric" position="left" grid="true" fields="valY"/>
<apex:axis title="X" type="Numeric" position="bottom" fields="valX" />
<apex:lineSeries title="{!chart}" axis="left" xField="valX" yField="valY"/>
</apex:chart>
</div>
</apex:outputPanel>
</apex:form>
</apex:define>
</apex:composition>
</apex:page>
PROS: Efficient and user friendly
CONS: None
The third solution is working great for me. However, when I get my button status changed, it only appears to change the button I click (either top or bottom). If I click the top button, the bottom button still allows me to click it. Am I missing a step?
ReplyDeleteHello Matt,Thanks for your comment.
DeleteIn third case the approach is to make DIV with id = "my-div" unavailable.
Make sure that your Bottom and Top Buttons are in this DIV, or add in "start" and "stop" attr of code which disabled all places with button.
Example:
onstart="$('#mydiv').addClass('disabledbutton');$('.statusbardiv').css('display','block');"
change to
onstart="$('#div-with-top-button').addClass('disabledbutton');$('#div-with-bottom-button').addClass('disabledbutton');$('.statusbardiv').css('display','block');"
Where '#div-with-top-button' and '#div-with-bottom-button' id's of DIVs where you put the buttons
All the best!
Hi there! Just like to ask if you think anything wrong with my div. Close button is not showing :)
ReplyDeleteHello! It's quite complicated to help you without looking to your code. Please use our "Contact Us" form if you want to us to help you to leverage "Disable and animate APEX command button" in your Salelforce instance. We might need to get access to your sandbox. Good luck!
DeleteHi,
ReplyDeletein option 4 is the rerender option needed for this option to woek?
I am asking as I have an inputFile function on the same VF page thought its another button ("insert") that i want to prevent the double clicks on. When I try option 4 I get an error saying rerender is not compatible with InputFile.?
cheers
George
Um, i meant option 3 in my last comment
ReplyDeleteGeorge
Hello,
ReplyDeleteTo help you we need to take a look at your code
Please Contact Us (you can use the form above), provide details and we will estimate the scope of work.
Looking forward to your response.
Regards,
WebSolo Team