I've noticed that when I have an <apex:selectList/>
with a value
attribute that calls a setter method to set a value in the controller all my getters get called before the initial set call happens.
This is causing issues for me because in some of my getters I set default values for other select lists, which defaults my changed selectList back to what it was initially.
I've isolated the code to demonstrate this behavior. Using the form below if you select False for both of the select lists and turn on debugging, when you change the second list from false to true the debug logs show the getters being called before the setter and rerender methods.
Why are the getters called before the setter that is starting the transaction? Why are getters called multiple times?
Visualforce:
<apex:page showHeader="true" sidebar="true" controller="DummyController">
<apex:form id="form">
<apex:actionFunction name="rerender" action="{!rerender}" reRender="form"/>
<apex:selectList value="{!select1Value}" onchange="rerender();" multiselect="false" size="1">
<apex:selectOptions value="{!select1Values}"/>
</apex:selectList>
<apex:selectList value="{!select2Value}" onchange="rerender();" multiselect="false" size="1" rendered="{!renderSelect2}">
<apex:selectOptions value="{!select2Values}"/>
</apex:selectList>
<apex:commandButton action="{!press}" disabled="{!disableButton}" reRender="form" value="Press"/>
</apex:form>
</apex:page>
Controller:
public class DummyController {
public String select1Value;
public String select2Value;
public DummyController() {
}
public List<SelectOption> getSelect1Values() {
System.debug('getSelect1Values');
SelectOption one = new SelectOption('true', 'true');
SelectOption two = new SelectOption('false', 'false');
return new List<SelectOption>{one, two};
}
public List<SelectOption> getSelect2Values() {
System.debug('getSelect2Values');
SelectOption one = new SelectOption('true', 'true');
SelectOption two = new SelectOption('false', 'false');
return new List<SelectOption>{one, two};
}
public String getSelect1Value() {
System.debug('getSelect1Value');
return this.select1Value;
}
public String getSelect2Value() {
System.debug('getSelect2Value');
return this.select2Value;
}
public Boolean getRenderSelect2() {
System.debug('getRenderSelect2');
return String.isNotBlank(this.select1Value);
}
public void setSelect1Value(String value) {
System.debug('setSelect1Value');
this.select1Value = value;
}
public void setSelect2Value(String value) {
System.debug('setSelect2Value');
this.select2Value = value;
}
public void rerender() {
System.debug('Rerendering');
}
public Boolean getDisableButton() {
System.debug('getDisableButton');
return String.isBlank(this.select1Value) || String.isBlank(this.select2Value);
}
public void press() {
System.debug('Button Pressed');
this.select1Value = null;
this.select2Value = null;
}
}
Debugs:
13:00:47.0 (7925137)|USER_DEBUG|[36]|DEBUG|getRenderSelect2
13:00:47.0 (8142382)|USER_DEBUG|[55]|DEBUG|getDisableButton
13:00:47.0 (8354192)|USER_DEBUG|[10]|DEBUG|getSelect1Values
13:00:47.0 (8542779)|USER_DEBUG|[26]|DEBUG|getSelect1Value
13:00:47.0 (8658064)|USER_DEBUG|[18]|DEBUG|getSelect2Values
13:00:47.0 (8792814)|USER_DEBUG|[31]|DEBUG|getSelect2Value
13:00:47.0 (8952937)|USER_DEBUG|[41]|DEBUG|setSelect1Value
13:00:47.0 (9016151)|USER_DEBUG|[36]|DEBUG|getRenderSelect2
13:00:47.0 (9111275)|USER_DEBUG|[46]|DEBUG|setSelect2Value <---- I would expect this to be at the top of the call stack. This event is essentially what is kicking off this transaction.
13:00:47.0 (9247100)|USER_DEBUG|[51]|DEBUG|Rerendering
13:00:47.0 (10992618)|USER_DEBUG|[36]|DEBUG|getRenderSelect2
13:00:47.0 (11661385)|USER_DEBUG|[10]|DEBUG|getSelect1Values
13:00:47.0 (11912811)|USER_DEBUG|[26]|DEBUG|getSelect1Value
13:00:47.0 (12036977)|USER_DEBUG|[18]|DEBUG|getSelect2Values
13:00:47.0 (12216899)|USER_DEBUG|[31]|DEBUG|getSelect2Value
13:00:47.0 (13380418)|USER_DEBUG|[55]|DEBUG|getDisableButton