[SalesForce] ‘Save’ Button and ‘Save & New’ Button in Custom Wizard Create Multiple Records if Clicked Multiple Times before New Page Is Loaded

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;

}

Best Answer

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>