Tuesday 7 August 2012

Visualforce Component that creates a picklist without a -none- value

About a year and a half ago, when I was relatively new to the Force.com platform, I devised a way to create a picklist value selector with a '-none-' value for a field that could be placed on to a Visualforce page, to prevent users from not setting a value for a picklist field.

A question was posted in that blog entry that asked if it was possible to have two picklists for different fields on the same Visualforce page. I replied with an answer that was basically "write the code out twice". While this works, its not exactly re-usable or easy to maintain.

Being a big fan of Visualforce components, I set about converting the no '-none-' picklist code into a re-usable component that can be placed onto any number of pages, and used in different applications easily. I found that this was possible by using the describe object and describe field calls.

The component I have created is called InputPicklistNoNone. It has two attribute arguments that have to be defined when using it in a Visualforce Page:
  • Value: This is the reference for the variable you want to assign the picklist selection to. It is much like the value attribute for most Visaulforce input components, such as apex:inputField and apex:inputText.
  • Field: This is the API reference of the picklist field you want to extract the possible values from. If this value points to an invalid field, an error message will be added to the page.
Below is the component code, and sample page and controller code to show how the component can be used. This is just a simple example using the component in a Visualforce Page and a standard controller with an extension. The component is flexible, it can be used with custom controllers and the value it populates can be an object field or a string variable.

Component Code:
<apex:component controller="InputPicklistNoNoneController">
  <apex:attribute name="value" required="true" type="String" 
   description="The variable the selected option will be assigned to"/>
  
  <apex:attribute name="field" assignTo="{!fieldName}"    
   required="true" type="String" 
   description="The picklist field that forms the basis of the input"/>

  <!--If the value is empty, then set it to be equal to the default 
      value from the schema description-->

  <apex:variable var="value" value="{!defaultOption}" 
   rendered="{!ISNULL(value)}"/>

  <apex:selectList size="1" value="{!value}">
    <apex:selectOptions value="{!options}"/>
  </apex:selectList>
</apex:component>

Component Controller:
public class InputPicklistNoNoneController 
{
    public String             defaultOption {get;set;}
    public List<SelectOption> options       {get;set;}
    public String             fieldName
    { 
        get;
        set 
        {
            fieldName = value;
            options = new List<SelectOption>();
            List<String> fieldNameSplit = fieldName.split('\\.');
        
            Schema.DescribeFieldResult picklistFieldDescription =
              Schema.getGlobalDescribe().get(fieldNameSplit[0])
              .getDescribe().fields.getMap()
              .get(fieldNameSplit[1]).getDescribe();

            for (Schema.Picklistentry picklistEntry:
                 picklistFieldDescription.getPicklistValues())
            {
                options.add(new SelectOption(pickListEntry.getValue(),
                                             pickListEntry.getLabel()));

                if (picklistEntry.defaultValue)
                {
                    defaultOption = pickListEntry.getValue();
                }
            }    
        }
    }    
}

Example Page:
<apex:page standardController="Invoice_Statement__c"
           extensions="InputPicklistNoNoneExampleExtension">
           
  <apex:sectionHeader title="No none picklist component example"/>
  
  <apex:form >
    
    <apex:panelGrid columns="2">
    
      
      <!-- The component can be used to populate an object field -->  
      <apex:outputText value="Status:"/>
      <c:InputPicklistNoNone value="{!Invoice_Statement__c.Status__c}" 
                             field="Invoice_Statement__c.Status__c"/>
    
      
      <!-- The component can also be used with a controller variable -->
      <apex:outputText value="Industry:"/>
      <c:InputPicklistNoNone value="{!myControllerVariable}"
                             field="Account.Industry"/>    
    
      <apex:commandButton value="Save" action="{!save}"/>
    
    </apex:panelGrid>
    
  </apex:form>  
</apex:page>

Example Controller Extension:
public class InputPicklistNoNoneExampleExtension 
{
    public String myControllerVariable {get; set;}

    public InputPicklistNoNoneExampleExtension(
           ApexPages.StandardController controller) {}

}

Screenshot of Example Page:

I am a big fan of how this has turned out, it keeps the functionality intact while making it easily adaptable for use on numerous variables on any number of pages, result! If you have any questions, or need some pointers on how to use it, then please add a comment below.

14 comments:

  1. This seems like it could be really useful to me right now since I'm struggling with a related problem. Unfortunately, my VF skills are a little rusty, so I could really use the controller for the component itself in addition to what you've displayed above.

    ReplyDelete
    Replies
    1. Hi Brian,

      No problem, yes I should probably have included the controller code for the component. I have now added it above, let me know if you have any questions about it!

      Cheers,

      Chris

      Delete
  2. Cheers for the very rapid response!

    In retrospect, it makes perfect sense but it would have taken me forever to figure out how to get from fieldName to the picklistFieldDescription!

    ... Now (1) I just need to figure out how to make the default value the current picklist value of an indicated record on a different type of sObject and (2) Apply this same wisdom to multipicklists...


    ReplyDelete
    Replies
    1. Hmmm, applying this idea to multipicklist fields doesn't really fit, as they don't have a '-none-' value to speak of. If you want to ensure that a value is always entered for a multipicklist field, I think that a validation rule is the only way to go. Its cure rather than prevention, but I believe its the only way.

      Delete
  3. Hi Chris -

    I am a SFDC newbie. How do I write test classes for the controller and component? Thanks.

    Cheers,
    Lino

    ReplyDelete
    Replies
    1. Hi there Lino, thanks for the question.

      If you want some general advice on getting started with writing test methods, then I can recommend the trailhead apex testing module which explains the fundamentals of the testing framework begin apex. https://developer.salesforce.com/trailhead/en/module/apex_testing

      In terms of writing tests with this specific functionality think about an example field that you want to use this for, and then call the method in the InputPicklistNoNoneController with the field name. Then check that the pick list values you get back are what you expect.

      I hope that helps, let me know if you have any more questions or need further clarification.

      Regards,
      CAL

      Delete
    2. This comment has been removed by the author.

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Great post i must say and thanks for the information. Education is
    definitely a sticky subject. However, is still among the leading topics of our time. I appreciate your post and look forward to more.
    salesforce consulting companies in india

    ReplyDelete
  6. Anytime birth and (Coach Outlet Clearance Sale) that i are perhaps hangin at Chad, (Understand it?) And other (Ray Ban New Wayfarer Polarized) excellent employees of or net, People around the globe make sure you snoop intently recommended to the educated labeling (Coach Outlet Store Online) of such discounts. If (Cheap Yeezys For Sale) i'd like to see something realistically (Michael Kors Outlet) delectable, Each I must do is (Coach Outlet Online) considered steer my personal way at nighttime, Over elegance, Sloping land, With a crowd (Ray Ban Outlet) out of alcoholic beverages (Michael Kors Outlet Online) lovers and ingrown toenail ditch adventures and dogs and kids whereas stab a taco or regardless of which while having these spear. I send credit score (New Yeezys 2020) to positively my personal GF, Daybreak, For kind of making everybody off from a good rear end one friday the evening, An hour that i place, Toxins sod, Atone for Z

    ReplyDelete
  7. AbstractWe perform a suite of smoothed particle hydrodynamics simulations to investigate in detail the results of a Yeezy Boost 350 giant impact on the young Uranus. We Coach Outlet Store study the internal structure, rotation rate, and atmospheric retention of the post impact planet, as well as Yeezy Discount the composition of material ejected into orbit. Most of the material from the impactor's rocky core falls in to the core of the target.

    Besides, there is research going on to use non chemical biological means to control Ray Ban Outlet locusts, according to FAO. "The current focus is primarily on pathogens and insect growth regulators. Thus far control by natural predators and parasites is limited since locusts can quickly migrate away from most natural enemies.

    Unlike folinic acid, the effectiveness of folic acid supplementation Coach Outlet was dependent on the E. Coli New Jordan Shoes 2020 gene, abgT, suggesting a bacterial route with PABA glu uptake by E. Coli as a first step. When it comes to talking animals, we used to watching the mouths move and sometimes, even the area around the eyes. In Mowgli, the animals faces are unnervingly expressive, which takes a while getting used to. Baloo, in particular, is given several small ticks he seems to have a Coach Handbags Clearance perpetual cold, and has Ray Ban Glasses the face of a bare knuckle boxer that add a whole new dimension to the character, which Serkis plays as a cross between Mr Miyagi from Karate Kid and Burgess Meredith from Rocky..

    ReplyDelete