Tweet |
Initially I was using the describe capability for the StageName field, which
worked fine for a single record type. However, as soon as I introduced
multiple opportunity record types with different sales processes, I hit
problems - regardless of the sales process, the full set of picklist values
was returned each time, even if I navigated to the field via an opportunity
with the appropriate record type. Some googling and searching on the
Developerforce discussion boards seemed to confirm that there isn't a simple
way to achieve this.
Clearly the information about which stages are applicable to a record type is
available to the Salesforce UI, as the Stage picklist contains the values
specific to the record type. Even better, this is respected when using a
Visualforce input field. Therefore one way of getting at this
information for use in a Visualforce controller is to have the page supply it.
Utilizing a technique I've written about before in DML During Initialisation, I created a page that would send the picklist values back to the controller
the first time it was loaded, and then display the details thereafter.
The first time that the page is loaded the following outputpanel is rendered:
This contains a hidden form with an inputfield for the opportunity stage and an input text element that will be used to submit the stage options to the controller. The reload() function is installed as an onload handler, and this extracts the option text, concatenates it into a single string value, populates the input text with this string and then submits the form.
When the page is reloaded, the following outputpanel is rendered - this simply creates a pageblocktable to iterate through the available values:
The controller is shown below - the heavy lifting is done by the reload() method - this parses the string containing the picklist values and stores them in a list property. it also sets the loadonce property to false, to ensure that the page is reloaded once and once only:
Here are a couple of screenshots of the page doing its thing for opportunities with two different record types. The first with just a few of the stages:
and the second with most of them:
For the sake of completeness, the entire page is shown below:
And a special thanks to the blogger software error that meant I had to write this post twice! Always save at regular intervals.
The first time that the page is loaded the following outputpanel is rendered:
<apex:outputPanel rendered="{!loadonce}"> <apex:form id="frm"> <apex:actionFunction name="reloadWithStages" action="{!reload}"/> <div id="test1" style="width: 100%; height: 150px;"></div> <apex:outputPanel layout="block" id="vals" style="display:none"> <apex:inputField value="{!Opportunity.StageName}" required="false" id="stages"/> <apex:inputText value="{!valsText}" required="false" id="back"/> </apex:outputPanel> </apex:form> <script> function reload() { var ele=document.getElementById('{!$Component.frm.stages}'); var idx=0; var valText=''; for (idx=0; idx<ele.length; idx++) { valText+=ele.options[idx].text + ':'; } var backele=document.getElementById('{!$Component.frm.back}'); backele.value=valText; reloadWithStages(); } window.onload=reload(); </script> </apex:outputPanel>
This contains a hidden form with an inputfield for the opportunity stage and an input text element that will be used to submit the stage options to the controller. The reload() function is installed as an onload handler, and this extracts the option text, concatenates it into a single string value, populates the input text with this string and then submits the form.
When the page is reloaded, the following outputpanel is rendered - this simply creates a pageblocktable to iterate through the available values:
<apex:outputPanel rendered="{!NOT(loadonce)}"> <apex:pageBlock title="Status Values for record type {!Opportunity.RecordType.Name}"> <apex:pageBlockTable value="{!pickListVals}" var="plVal"> <apex:column headerValue="Stage"> <apex:outputText value="{!plVal}"/> </apex:column> </apex:pageBlockTable> </apex:pageBlock> </apex:outputPanel>
The controller is shown below - the heavy lifting is done by the reload() method - this parses the string containing the picklist values and stores them in a list property. it also sets the loadonce property to false, to ensure that the page is reloaded once and once only:
public with sharing class RecordTypePickListController { public List<String> pickListVals {get; set;} public String valsText {get; set;} public Boolean loadOnce {get; set;} private Opportunity opp; public RecordTypePickListController(ApexPages.StandardController std) { opp=(Opportunity) std.getRecord(); loadOnce=true; } public PageReference reload() { pickListVals=new List<String>(); Boolean skip=true; for (String val : valsText.split(':')) { if (skip) { skip=false; } else { pickListVals.add(val); } } loadOnce=false; return null; } }
Here are a couple of screenshots of the page doing its thing for opportunities with two different record types. The first with just a few of the stages:
and the second with most of them:
For the sake of completeness, the entire page is shown below:
<apex:page standardController="Opportunity" extensions="RecordTypePickListController"> <apex:outputPanel rendered="{!loadonce}"> <apex:form id="frm"> <apex:actionFunction name="reloadWithStages" action="{!reload}"/> <div id="test1" style="width: 100%; height: 150px;"></div> <apex:outputPanel layout="block" id="vals" style="display:none"> <apex:inputField value="{!Opportunity.StageName}" required="false" id="stages"/> <apex:inputText value="{!valsText}" required="false" id="back"/> </apex:outputPanel> </apex:form> <script> function reload() { var ele=document.getElementById('{!$Component.frm.stages}'); var idx=0; var valText=''; for (idx=0; idx<ele.length; idx++) { valText+=ele.options[idx].text + ':'; } var backele=document.getElementById('{!$Component.frm.back}'); backele.value=valText; reloadWithStages(); } window.onload=reload(); </script> </apex:outputPanel> <apex:outputPanel rendered="{!NOT(loadonce)}"> <apex:pageBlock title="Status Values for record type {!Opportunity.RecordType.Name}"> <apex:pageBlockTable value="{!pickListVals}" var="plVal"> <apex:column headerValue="Stage"> <apex:outputText value="{!plVal}"/> </apex:column> </apex:pageBlockTable> </apex:pageBlock> </apex:outputPanel> </apex:page>
And a special thanks to the blogger software error that meant I had to write this post twice! Always save at regular intervals.
Bob, this causes two entries in the browser history, so the back button does not go back to the previous page, you have to jump back two pages (in FF9.0.1)
ReplyDeleteYeah, the action function isn't carrying out a rerender, it's just refreshing the page. An exercise for the avid student!
DeleteThanks. That worked great.
ReplyDeleteBob,
ReplyDeleteCan you tell me how to get the dependent picklist values in apex based on the Controlling picklist value?
We are using AngularJs and Bootstrap so we dont want to use standard salesforce tag for dependent picklist.
Thanks in advance
@Karthik,
DeleteDid u get solution for your issue, I m having exactly same issue.
Thanks
@Karthik,
DeleteDid u get solution for your issue, I m having exactly same issue.
Thanks
Hope this link will help!!
Deletehttp://titancronus.com/blog/2014/05/01/salesforce-acquiring-dependent-picklists-in-apex/
Hi Bob,
ReplyDeleteI have an alternative solution, your thoughts please..
Add the picklist values as numeric codes like 0 to 9 associate them with Record Type1 and 10 to 19 associate them with Record Type2.
In our code, loop in based on these numeric codes only to show 0-9 or 10-19 based on the record Type name.
Use Translations in salesforce to load the exact labels for these numeric codes which the end users would like to see as list of values.
Does that sound good?
Regards,
Rupesh Bhatia
The problem I have with solutions of this nature is that it puts the onus on administrators to remember to create the picklist values according to the rules, and it means the code relies on the rules having been followed. It will work, but I prefer to remove the human element from the process.
DeleteAlso these numbers will display in reports and users who are not using your VF page will see them too. (standard Salesforce pages). Not user friendly.
DeleteHi Bob,
ReplyDeleteIs there any work around for getting default Picklist values for different record types.
Like I have 5 opportunity record types and I have different default picklist value of picklists with different record types.
can I fetch default picklist values with record type combinations?
Thanks,
Sanjeev