+ Start a Discussion
parkerAPTparkerAPT 

How to disable button on Click and Renable on completed AJAX request

I'd like to prevent users from repeatedly clicking on a custom button I've created.

Right now I have a button that will create a custom task via an AJAX call on the contacts page. On the returning AJAX call, I reRender the list of Activites associated with this contact.

However, I need a way to provide visual feedback to disable the button after a user clicks it.

I've seen this work on QuickSave button and am wondering how to implement this.

Thanks
-P

Here is the code for reference. (I've removed the inital "
apex:page standardController="Contact" extensions="customController" tabStyle="Contact">
apex:detail subject="{!contact}" relatedList="false"/>
apex:form >
apex:commandButton reRender="activitiesPanel" status="activityStatus" action="{!markContactAsSentEmail}" value="Add task for contact"/>
/apex:form>
apex:outputPanel id="activitiesPanel">
apex:actionStatus id="activityStatus" startText="Refreshing Activity History...">
apex:facet name="stop">
apex:relatedList id="activitiesList" list="ActivityHistories" />
/apex:facet>
/apex:actionStatus>
/apex:outputPanel>



Message Edited by parkerAPT on 12-23-2008 02:15 PM
Best Answer chosen by Admin (Salesforce Developers) 
MohanaGopalMohanaGopal

Hi..

 

This may help u...

VF code
<apex:actionStatus id="AjaxStatus" onstart="DisableButton()" onstop="EnableButton()" ></apex:actionStatus>
<apex:commandbutton value="Test Button" id="btnTest"/>

<script>
function DisableButton()
{
document.getElementById("{!$Copmponent.btnTest}").disabled=true;
}
function EnableButton()
{
document.getElementById("{!$Copmponent.btnTest}").disabled=false;
}

</script>
 
 <apex:actionsupport action="{!urajaxaction}" event="onblur" rerender="info" status="AjaxStatus"/>


 
Use Actionstatus id when u call any ajax call like actionsupport..

All Answers

Arun BalaArun Bala
Hi Parker,
I would do this .. my changes are in bold.

<apex:commandButton reRender="activitiesPanel,idActStatButt" disabled="{!buttonStatus}" status="activityStatus" action="{!markContactAsSentEmail}" value="Add task for contact" id="idActStatButt"/>

On the constructor of the controller, I would set the buttonStatus = false. And on the markContactAsSentEmail method, I would set buttonStatus = true.

HTH.
parkerAPTparkerAPT
Thanks for the reply.

I think your solution solves a different problem. Your solution will set the button to be disabled after the AJAX call returns and reRenders. Am I misinterpreting your solution, or is this how it was designed?

However, I only want the button to be disabled during the processing of the AJAX call returns. Users should be able to resubmit the button after processing occurs but not during processing.

Furthermore, is there a way to disable all actionable items within an actionRegion while an AJAX call is processing?

Thanks
P
MohanaGopalMohanaGopal

Hi.. Parker.

              Try onstart ,onstop property in actionstatus tag..

U call js function  for disable ur button in onstart and enable ur btn in onstop ..

 

I hope its make sense...

parkerAPTparkerAPT
I understand how I can run arbitrary Javascript before or after the AJAX request, however, I haven't been able to find any example that illustrates how I can get a handle on the button by using javascript.

Do you have any examples of this?
MohanaGopalMohanaGopal

Hi..

 

This may help u...

VF code
<apex:actionStatus id="AjaxStatus" onstart="DisableButton()" onstop="EnableButton()" ></apex:actionStatus>
<apex:commandbutton value="Test Button" id="btnTest"/>

<script>
function DisableButton()
{
document.getElementById("{!$Copmponent.btnTest}").disabled=true;
}
function EnableButton()
{
document.getElementById("{!$Copmponent.btnTest}").disabled=false;
}

</script>
 
 <apex:actionsupport action="{!urajaxaction}" event="onblur" rerender="info" status="AjaxStatus"/>


 
Use Actionstatus id when u call any ajax call like actionsupport..

This was selected as the best answer
parkerAPTparkerAPT
Thanks Mohana, this was very helpful.

I ended up modifying your functions slightly to also change the CSS classes. Here is the code for others:


function disableButton(el){
var btn = document.getElementById(el);
if(btn!==null){
btn.disabled = true;
btn.className = 'btnDisabled';
}
}
function enableButton(el){
var btn = document.getElementById(el);
if(btn!==null){
btn.disabled = false;
btn.className = 'btn';
}
}
rksfdcrksfdc
Code:
<apex:actionStatus id="AjaxStatus" onstart="DisableButton()" onstop="EnableButton()" ></apex:actionStatus>
                <apex:commandButton id="btnSearch" value="Search">              
                </apex:commandButton>
                <script>
     function DisableButton()
     {
      document.getElementById("{!$Component.frmMain.pbSearch.pbbSearch.btnSearch}").disabled=true;
     }
     function EnableButton()
     {
      document.getElementById("{!$Component.frmMain.pbSearch.pbbSearch.btnSearch}").disabled=false;
     }
    </script>
     
    <apex:actionsupport action="{!searchAccounts}" event="onblur" rerender="btnSearch,frmMain,msgs" status="statusResults"/>

 
I am trying to do the samething, but with little success. Can you guys pl skim thru the code and let me know if there is anything wrong. Thanks in advance.
parkerAPTparkerAPT
Two Things:
  • I think you need don't normally need to use actionSupport for components like commandLink or commandButton because you can directly specify the action.  This action gets fired when a user clicks on the button / link.  You should use actionSupport for things that do not directly support the action attribute or if you want to attach the action to an event other than click.  (like on blur in your case).  I've modified the code to reflect the changes.
  • I don't think you need to specify the entire chain of ids like: "$Component.frmMain.pbSearch.pbbSearch.btnSearch"
    • I've modified the code by replacing this with: $Component.btnSearch
So your code would look like:

Code:
<apex:actionStatus id="AjaxStatus" onstart="DisableButton()" onstop="EnableButton()" ></apex:actionStatus>
<apex:commandButton status="AjaxStatus" action="{!searchAccounts}" id="btnSearch" rerender="btnSearch,frmMain,msgs" value="Search">              
</apex:commandButton>
<script>
     function DisableButton()
     {
      document.getElementById("{!$Component.btnSearch}").disabled=true;
     }
     function EnableButton()
     {
      document.getElementById("{!$Component.btnSearch}").disabled=false;
     }
</script>


Let me know if this works because I have not testing it.
 



Message Edited by parkerAPT on 01-14-2009 12:55 PM
rksfdcrksfdc

Hi,

Thanks for your help, that worked. Couple of thing that I noticed was,

- I had the Command Button inside the PageBlockSection so it was only disabling the top one not the bottom one, so now I placed the command button outside the pageblock section.

- In IE you can visually see that the button has been disabled, but in FireFox even though it's disabled, it looks like the button is enabled.

Thanks again.

parkerAPTparkerAPT
Inside your disableButton function, also change the CSS class from enabled to disabled just like I did in my post a few above yours.

btn.className = 'btnDisabled';

btn.className = 'btn';


The following link illustrates some of the button classes in Salesforce by default:
http://www.salesforce.com/us/developer/docs/pages/Content/pages_styling_salesforce.htm#kanchor240
rksfdcrksfdc
Thanks for your help again, that fixed it.
rksfdcrksfdc
Hi ParketAPT,
I am going to go a-head and ask one more question, sorry. See my code below, I have another commandbutton inside a different pageblock, when I try to disable that I am getting "document.getElementById is null". Any thoughts on this?
 
 
 
Code:
   <center>
    <apex:commandButton status="statusResults" action="{!searchAccounts}" onClick="DisableButton()" id="btnSearch" rerender="btnSearch,pbSearch,pbbSearch,frmMain,msgs" value="Search">
    </apex:commandButton>
   </center>
   <script>
    document.onkeypress = goSearch;
     
    function goSearch(e)
    {
     var key;
  
     if(window.event)
      key = window.event.keyCode; //IE
     else if(e)
      key = e.which;              //Firefox
     else
      return true;
  
     if(key == 13)
     {
      var component = "{!$Component.btnSearch}";
      document.getElementById(component).click();
      return false;
     }
     else
      return true;
    }
     
        function DisableButton()
        {
     document.getElementById("{!$Component.btnSearch}").disabled=true;
     //if({!hasSearched} == true)
     //{
     // document.getElementById("{!$Component.frmMain.pbMatchingResults.btnCreateNew}").disabled=true;     
     //}
     document.getElementById("{!$Component.btnSearch}").className = 'btnDisabled';
        }
        function EnableButton()
        {
     document.getElementById("{!$Component.btnSearch}").disabled=false;
     document.getElementById("{!$Component.btnSearch}").className = 'btn';
        }
   </script>
        </apex:pageBlock>
        
        <apex:pageBlock id="pbMatchingResults" title="Matching Results" mode="detail" rendered="{!hasSearched}">

            <apex:pageBlockButtons >
                <apex:commandButton id="btnCreateNew" value="Create New" action="{!newAccount}" disabled="{!searchEnabled}"/>
            </apex:pageBlockButtons>
            <apex:actionStatus id="statusResults" startStyle="font-weight:bold; color:green;" startText="Loading..." onstart="DisableButton()" onstop="EnableButton()">

 
rksfdcrksfdc
you can see the 3 lines that are commented out, that's causing this error message.
parkerAPTparkerAPT
I have never used these lengthy Component references like:
{!$Component.frmMain.pbMatchingResults.btnCreateNew}

Have you tried:

document.getElementById("{!$Component.btnCreateNew}").disabled=true;
rksfdcrksfdc
yes, initially I had the reference as Component.btnCreateNew, since that didn't work I changed it to Component.frmMain.pbMatchingResults.btnCreateNew, and that's not working either. Will post here if I was able to fix it. thx
parkerAPTparkerAPT
I think I figured it out.

The $Component notation you need to specify ID structure relative to where the $Component notation exists.  If you think of the DOM as a tree structure, you will need to specify a common ancestor and then traverse down each tree branch using the named Ids.

I know that isn't a great explanation, but hopefully the following example will demonstrate how $Component resolves which ID you actually are referencing.

The Two Key examples from this that should help you are the $Component reference in the onclick for button:
btnAlertTwo_Outside and alertOutsideDom.

Also note that you will NEED to have an ID on the pageBlockButtons. Notice that the onclick $Component reference for
btnAlertThree_Outside cannot resolve the id of:Component.block2.btnAlertFour

Code:
<apex:form id="TheForm">
<apex:pageBlock title="Block1" id="block1">
 <apex:pageBlockButtons location="top" id="blkBtns">
  <apex:commandButton id="btnAlertOne_Inside1" value="Alert This Buttons Id" onclick="alert('{!$Component.btnAlertOne_Inside1}')"/>
  <apex:commandButton id="btnAlertTwo_Inside1" value="Alert Neighboring ID" onclick="alert('{!$Component.btnAlertTwo_Inside1}')"/>
  <apex:commandButton id="btnAlertThree_Inside1" value="Alert Button in Block2 ID" onclick="alert('{!$Component.btnCreateNew_Inside2}')"/>
  <apex:commandButton id="btnAlertFour" value="Button Alert Four" onclick="alert('{!$Component.btnAlertFour}')"/>
 </apex:pageBlockButtons>
</apex:pageBlock>
<apex:pageBlock title="Block2" id="block2">
 <apex:pageBlockButtons location="top">
  <apex:commandButton id="btnCreateNew_Inside2" value="Alert This Buttons Id" onclick="alert('{!$Component.btnCreateNew_Inside2}')"/>
  <apex:commandButton id="btnAlertFour" value="Button Alert Four" onclick="alert('{!$Component.btnAlertFour}')"/>
  <apex:commandButton id="alertOutsideDom" value="Alert a button On another Dom branch" onclick="alert('{!$Component.TheForm.btnAlertOne_Outside}')"/>
 </apex:pageBlockButtons>
</apex:pageBlock>
<apex:commandButton id="btnAlertOne_Outside"  value="Alert Block1 Button One ID" onclick="alert('{!$Component.btnAlertOne_Inside1}')"/>
<apex:commandButton id="btnAlertTwo_Outside"  value="Alert Block1 Button Four" onclick="alert('{!$Component.block1.blkBtns.btnAlertFour}')"/>
<apex:commandButton id="btnAlertThree_Outside"  value="Alert Block2 Button Four" onclick="alert('{!$Component.block2.btnAlertFour}')"/>

</apex:form>

 In your case, add an id to the PageBlockButtons in pbMatchingResults to something like: blkbtns.
Your $Component reference should now hopefully look something like:

$Component.frmMain.pbMatchingResults.blkbtns.btnCreateNew

Hope that helps.



Message Edited by parkerAPT on 01-16-2009 10:38 AM
rksfdcrksfdc
you are right, providing the id for pageblockbutton and using it in component reference did the trick, thanks for the research and help. But did you notice one thing, if the button is a pageblock button and if you are disabling using the JavaScript, only the top commandbutton gets disabled, the bottom one doesn't, to overcome this I am making the button location only as top, I have mentioned about this in my previous post also. thanks for all your help, learnt some useful things here.