Showing posts with label component. Show all posts
Showing posts with label component. Show all posts

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.

Friday, 25 May 2012

Summer '12 release component layout attribute

*Special thanks to Abhinav Gupta on this one, without his blog post on his Summer'12 release favourites, this problem would have taken a lot longer to diagnose*

In the upcoming Summer '12 Salesforce release, one of the new features is a layout attribute for Apex component tags. This new layout feature allows you to wrap your components in a div or span tag. The full definition can be found in the release notes under "Visualforce Enhancements". It states that the default value for the new attribute is "inline" (span tag), so any existing component where this attribute does not exist should be unaffected, as is the Salesforce release way.

However, I have found in some cases that this new attribute has had an adverse affect on the appearance of Visualforce pages and email templates that leverage components.

For example, if you have an email template that that has a text attachment defined in Visualforce, and that definition has components included, span tags will now appear as part of the attachment. Take this simple email template and component definition.

Visualforce Email Template:
<messaging:emailTemplate subject="Sample Email With Text Attachment" 
                         recipientType="Contact" >

<messaging:plainTextEmailBody >
Wow look at this attachment, It's nearly as amazing as hoverboards!
</messaging:plainTextEmailBody>

<messaging:attachment renderAs="text/plain" filename="test.txt">
Hi {!recipient.Name}, Here is your awesome text!
<c:TextComponent />
</messaging:attachment>

</messaging:emailTemplate>


Visualforce Compnent Defintion:
<apex:component access="global">
Text from a component, YES! YES! YES!
</apex:component>

If an email message using this template is sent out this is how it appears in the recipient inbox if the Salesforce Org uses the Summer '12 release:



Here is the text attachment. As you can see it contains span tags, which is not what was intended. The same template would not have produced these tags in previous releases.



The way to resolve this is simple, just add the new layout attribute to the apex component definition, with a value of "none".
<apex:component access="global" layout="none">
Text from a component, YES! YES! YES!
</apex:component>

Now when an email using this template is sent, the email attachment looks like we want:


Summer '12 is currently in sandbox orgs, but will be promoted to all orgs over the next few weeks. My advice would be to identify the pages and templates where components are used before the release, making sure they still behave as intended. If they don't, add the layout attribute with a value of "none" as above.