Getter Method Order and Visualforce Pages
I recently had a Visualforce page that was working perfectly for over two years. This thing was solid, a workhorse, one of the most used pages I have ever created. Then one day it stopped working as expected. It wasn’t a big deal really, simply an warning message wasn’t being displayed to the users. All unit tests still passed and I had made no major changes to the code. What the crazy I thought?!? The only thing I may have done at some point in the past was make minor changes to the the layout or updated the page API version.
The way I architected the page from the very first day created a ticking time bomb that would show up two years after the page was created. Enter the world of Visualforce page generation and getter method order. Before I get to the juicy good stuff I’ll briefly explain how Visualforce pages are created…at a very high level.
1) Apex constructor code is run
2) Page generation begins
3) As the page starts to convert Visualforce markup to html it will see certain markup ( {!showWarning} ) on the Visualforce page related to variables in the controlling class
4) The page generation engine will be like, “Yo, controller, what up? Send me the variable showWarning” or “Hey, controla-homie, send me the list of accounts”. This is the GET of a specific variable.
5) The page generation engine can then embed the variable on the page as html output or perhaps use it to determine if pieces of the page should be rendered at all, such as a warning message
6) HTML is then fully created and sent to the browser making the request
Next lets take a little quiz. If you think quizzes are lame (I do) pretend this is a challenge of intellect that if solved will save the world from invading aliens. Ya, that’s a little weird and makes my sound crazier than I thought it would but let’s run with it. Take a look at the the code below. First the page:
<apex:page Controller="GetOrder"> <apex:pageMessage title="Large Account Alert" severity="warning" strength="1" rendered="{!showLargeAcctWarning}"/> <apex:pageBlock > <apex:pageBlockTable value="{!accounts}" var="a"> <apex:column value="{!a.Name}"/> <apex:column value="{!a.Name}"/> <apex:column value="{!a.NumberOfEmployees}"/> </apex:pageBlockTable> </apex:pageBlock> </apex:page>
And then the class:
public class GetOrder{ public Boolean showLargeAcctWarning {get; set;} List<Account> accts; public List<Account> getAccounts(){ if(accts == null){ accts = new List<Account>(); for(Account a : [Select Id, Name, NumberOfEmployees from Account limit 5]){ accts.add(a); if(a.NumberOfEmployees > 5){ showLargeAcctWarning = true; } } } system.debug(showLargeAcctWarning); return accts; } }
Do you see anything wrong? If not, then you are a n00b and I laugh at your n00biness while I sit upon my white stallion. Just kidding, I’ve been working with Visualforce since the beginning and I only recently realized what is wrong with the code above. Not kidding about sitting upon a white stallion though. If you do see a problem you are officially a l337 \/I5U4lf0(3 haxor.
So what is the problem? The page should display a warning message if the Number of Employees is greater than 5, right? Wrong! Well… maybe. You can view the actual page above be clicking here. You will see the warning message is not displayed but the list definitely has accounts with more that 5 employees? What is going on here…..queue the Twilight Zone music (just to be clear, the Twilight ZONE, not the dumb sparkly vampire Twilight), you have just crossed over into the Visualforce Getter Zone, dun dun dunnnn.
Let’s take a look at the debug log to see what is happening. First let’s check the debug statement above to see if the show warning variable is being set to true.
Hmm, the variable is definitely being set to true so why isn’t my warning being displayed. Let look a little deeper in to the log. Using the new Apex CSI log lets filter down to our get methods.
If we look at this closely we will see the get method for the showLargeAcctWarning is called before the getAccounts methods. So when the page rendering and asks for the show warning variable it is false as it has not yet been set to true because the getAccounts method has not even executed yet.
So how to resolve this issue. Enter our knight in shining armor, the constructor. Rather than letting the page determine the order of get methods lets run them ourselves in the class constructor. When you execute code in the constructor it is guaranteed to run before the page generation process begins. Below is the updated code.
public class GetOrderFix { public Boolean showLargeAcctWarning{get; set;} List<Account> accts; public GetOrderFix(){ //This is the contructor, same name as class, will run before page generation begins getAccounts(); } public List<Account> getAccounts(){ if(accts == null){ accts = new List<Account>(); for(Account a : [Select Id, Name, NumberOfEmployees from Account limit 5]){ accts.add(a); if(a.NumberOfEmployees > 5){ showLargeAcctWarning = true; } } } system.debug(showLargeAcctWarning); return accts; } }
Now the getAccounts method is will run before page generation begins and when the generation engine asks for the show warning variable it will have a value of true. You can see the fixed paged here.
What can make these types of issues so hard to track down is that the order in which get methods are called have no official or documented process. What seems like minor change to your Visualforce markup could change the order in which get methods are called and this could cause your page to stop working properly. What I have started to do, and is probably a best practice I should have always been doing, is setup all initial variables, lists, etc in the constructor. Do this and it should minify your problems… at least with getter method order.
