0

I have a simple visualforce page and controller - clicking the button logs the value of boolean instance variable 'mybool' then flips the value.

When I click the button rapidly the instance variable used by the function called by the second click hasn't been updated by the first click, but when I click it slowly it has.

If i click the button once I get -

12:42:54:004 USER_DEBUG [16]|DEBUG|my bool before -

12:42:54:004 USER_DEBUG [17]|DEBUG|true

Then I click it again (slowly) -

12:43:07:066 USER_DEBUG [16]|DEBUG|my bool before -

12:43:07:066 USER_DEBUG [17]|DEBUG|false

So the second click is getting the variable after it has been updated by the first click.

However, if I click it twice, fast I get -

First click:

12:43:18:007 USER_DEBUG [16]|DEBUG|my bool before -

12:43:18:007 USER_DEBUG [17]|DEBUG|true

Second Click:

12:43:19:006 USER_DEBUG [16]|DEBUG|my bool before -

12:43:19:006 USER_DEBUG [17]|DEBUG|true

So, the second click is reading the instance variable from somewhere else before it has had an opportunity to be updated by the first click. When do the instance variables get updated, and is there any way to ensure that the second click waits for the first update in controller?

controller:

public class sotestcontroller {
    public boolean myBool {get; set;}

    public sotestcontroller(){
        this.myBool = true;
    }
    public void toggleBool(){
        System.debug('myBool Before -');
        System.debug(this.myBool);
        this.myBool = !this.myBool;     
    }

page:

<apex:page controller="sotestcontroller">
<apex:form>
    <apex:commandButton action="{! toggleBool }" value="Submit"/>
</apex:form>
</apex:page>
1
  • 1
    This is a very common problem on any application, especially web apps. The best you can do is to disable the button in the onclick javascript handler and enable it again in the oncomplete handler. Commented Mar 9, 2020 at 13:00

1 Answer 1

1

This happens because the "view state" has to be passed from client to controller. If the page hasn't finished processing the previous transaction, the previous "view state" will still be loaded, thus repeating the action a second time. As stated by rael_kid, the solution is to disable input until the view state is updated.

Example:

<apex:page controller="sotestcontroller">
<apex:form id="form">
    <apex:actionStatus id="status">
      <apex:facet name="stop">
    <apex:commandButton action="{! toggleBool }" value="Submit" reRender="form" status="status" />
    </apex:facet>
    <apex:facet name="start">
      Working...
    </apex:facet>
</apex:form>
</apex:page>

This is just a simple demo; you can show a spinner, disable input, etc, until the page finishes performing its actions.

2
  • So, just to double check, none of the variables are stored server side - they all get sent as view state, which is stored on client, each time anything happens? Is this how mvc frameworks work in general?
    – eaeaoo
    Commented Mar 9, 2020 at 13:26
  • 1
    @eaeaoo Generally, yes, many servers and technologies prefer client-side view state. I know that ASP, JSP, and PHP (and others) all have the concept of server-side sessions, but they're all pretty limited in size and functionality. It's usually easier and more efficient to leverage a client's memory to maintain state, as it can get extremely costly in terms of memory to store all this on the server, plus answering tough questions like "how long to cache", etc. Client-side is a nice balance between "unlimited" cache time and reduction of server resources.
    – sfdcfox
    Commented Mar 9, 2020 at 14:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .