0

I'm creating a custom wizard for a custom object. There is a 'Save' button which saves the record and redirects to the parent object. There is a 'Save & New' button which saves the record but remains on the page and creates a new record. So far so good except one rather irritating issue: if the 'Save' button is clicked multiple times before being redirected to the parent record, then multiple copies of the child record are saved, likewise for the 'Save & New' button.

I've tried using <apex:actionStatus> with <apex:facet> . This nicely disabled the button after clicking it and rerendered the button from 'Save' to 'Saving...' but ultimately didn't redirect to the desired page nor saved the record.

I've also tried adding a Boolean flag in my controller and changing its value in my save() method. Trying both:

<apex:commandButton id="savebutton" action="{!save}" value="Save" rendered="{!flag}"/> as well as: <apex:commandButton id="savebutton" action="{!save}" value="Save" disabled="{!flag}"/>. But still multiple clicks would save multiple records.

I would prefer using the <apex:actionStatus> in my VF page, but can't get it to redirect properly. I think it has to do with rerendering overriding the page reference redirect.

Here is the save method in my controller as is without a flag:

public PageReference save(){
  PageReference parentPage = new ApexPages.StandardController(parent).view();      
  parentPage.setRedirect(true);

  try{
     insert component;
  } 
  catch(System.Exception e){

      ApexPages.addMessages(e);
      return null;

  }

  return parentPage;

}
  • 1
    share your controller method as well. – Tushar Sharma Oct 14 '16 at 16:47
2

Your button should control itself client-side:

    <apex:commandButton action="{!saveAndRedirectSample}" reRender="form" 
        status="status" onClick="this.disabled=true; this.className='btnDisabled';"
        onComplete="this.disabled=false; this.className='btn';" 
        value="Save And Redirect"/>

The VF framework should be able to redirect even in the event of a reRender attribute, so you may simply have a bug somewhere.

I've written a trivial example that demonstrates this:

public class DemoRedirectController {
    public PageReference saveAndRedirectSample() {
        Long startTime = System.now().getTime();
        while(System.now().getTime()-startTime<2000);
        return new PageReference('https://www.google.com/');
    }
}

<apex:page controller="DemoRedirectController">
    <apex:form id="form">
        <apex:actionStatus startText="Working..." id="status" />
        <apex:commandButton action="{!saveAndRedirectSample}" reRender="form" 
            status="status" onClick="this.disabled=true; this.className='btnDisabled';"
            onComplete="this.disabled=false; this.className='btn';" 
            value="Save And Redirect"/>
    </apex:form>
</apex:page>
  • When I tried this in Chrome it left me with a blank page after the pause. In IE threw "This content cannot be displayed in a frame" error – Josh D Oct 14 '16 at 17:57
  • 1
    @JoshD Are you trying this in SF1 or LEX? I'm pretty sure it's just the example URL that I used. Try redirecting to a page within Salesforce and see if that fixes things. – sfdcfox Oct 14 '16 at 18:03
  • I'm trying this in Classic actually. After you mentioned I tried it in LEX, strangely enough it redirects properly to the parent object's detail page (although not to google.com) but it still doesn't work in Classic. – Josh D Oct 14 '16 at 18:16
  • @JoshD That's odd. Maybe it's just the context it's in, but it should be working anywhere in Classic. – sfdcfox Oct 14 '16 at 18:31
  • @JoshD Using the onClick JS should probably prevent your multiple save issue, though, even without the reRender attribute. Have you tried that? – sfdcfox Oct 14 '16 at 18:36
0

As I mentioned in comments above to @sfdcfox's response, his solution worked perfectly in LEX but not in Classic mode which is a requirement of mine.

Here is another stack exchange question addressing this here. The last response by @NSjonas worked for me though no other solutions on that page did. It also worked for a 'Save & New' button without any need to re-enable the button.

However, this solution works only on one of the buttons in the pageBlock. That is if I should click the Save button at the top of the pageBlock it will be disabled and display 'Saving...' and redirect, but before it directs the Save button at the BOTTOM of the pageBlock remains enabled and still displays 'Save'. There is only a fraction of time before the page redirects but I can click the top button followed by the bottom button and save 2 records. I find this an unlikely issue from real users but the possibility remains.

Here is the VF mark up and JS:

<script src="//ajax.googleapis.com/ajax/libs/jquery/latest/jquery.js"></script>
<script>
   function disableOnSubmit(input){
    var btn = input;
    setTimeout(function(){btn.disabled = 'disabled';btn.className = 'btnDisabled';btn.value="Saving...";},50);
    }
</script>
<apex:form id="form">
 <apex:pageBlock title="My Object Edit" mode="edit">
  <apex:pageBlockButtons >
   <apex:commandButton action="{!save}" value="Save" onclick="disableOnSubmit(this);" /> 
   <apex:commandButton action="{!savenew}" value="Save & New" onclick="disableOnSubmit(this);" /> 
  </apex:pageBlockButtons>
<!-- page block section and input fields -->
</apex:pageBlock></apex:form>

And here are my the functions in my controller:

public PageReference save(){
  PageReference parentPage = new ApexPages.StandardController(parent).view();      
  parentPage.setRedirect(true);
  try{
     insert component;
  } catch(System.Exception e){
      ApexPages.addMessages(e);
      return null;}
  return parentPage;
} 

public PageReference savenew(){
  try{
     insert component;
   } catch(System.Exception e){
      ApexPages.addMessages(e);
      return null;}
  component = new Consumable_Analysis_Component__c();
  component.PCS_Solution_Analysis__c = parentId;
  return null;
}

I'm satisfied with this solution but would be intrigued if there is any insight as to how to disable BOTH top and bottom buttons in the pageBlock.

  • the disableOnSubmit function in the linked question performs one key action, the button does not get disabled before the form POST takes place. In that example setTimeout is asynchronously disabling the button 50ms after clicking it. This is generally enough time for the browser to post the form. If you disable the button prior to the POST, the button does not get sent as one of the form elements and the page controller is unaware of which button was clicked. – Mark Pond Oct 25 '16 at 16:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.