Spring 11 is around the corner and Dave Carroll and I presented a webinar on the new platform features in Spring 11 a couple of weeks ago. We demoed some of the cool new features during the webinar and I've been sharing the code for those demos since then. As a follow-up to my post re Chatter triggers, I'd like to discuss the Field Sets demo that we did during the webinar. You can jump to this point in the recording if you're only interested in the Field Sets section of the webinar (though you'll miss Dave talking about his sock knitting if you do).

To recap, Field Sets is a new feature introduced in Spring 11 that allows Salesforce Admins to customize a Visualforce (aka VF) page via simple point-and-click. Field Sets therefore combine the power of Visualforce (the ability to create any custom UI) with what has always been a defining trait of the Force.com platform – the ability for end users/Admins to customize the application via config vs code. This new feature should be especially interesting for ISV developers who can now include 'generic/default' VF pages in their Managed Apps and let individual Admins determine which fields are applicable to their business and should therefore be visible on the VF page.

 

Here's a screenshot of the simple Account search VF page that I demoed during the webinar. The page has a search field for searching Account Names, followed by a section for displaying the search results. If the user selects any Account in the search results, the final section of the page displays some key fields for the selected Account. This last section is what uses a Field Set to determine which Account fields should be displayed on the VF page.

Here's the VF markup and associated controller code.

 

 

 

 

<apex:page standardController="Account" extensions="DynamicAccountExt">
<apex:form id="myForm">
<apex:pageBlock title="Search Criteria" >
<apex:pageBlockSection columns="1">
<apex:pageBlockSectionItem >
<apex:outputText value="Account Name"/>
<apex:inputText value="{!acctName}"/>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
<apex:pageBlockButtons >
<apex:commandButton action="{!doSearch}" value="Search"
rerender="searchResult,acctDetails,errors"/>
</apex:pageBlockButtons>
</apex:pageBlock>
<apex:pageBlock id="searchResult" title="Search Results" >
<apex:messages id="errors"/>
<apex:pageBlockTable value="{!searchResult}" var="acct" >
<apex:actionSupport event="onRowClick" action="{!refreshAcctDetails}"
rerender="acctDetails, errors">
<apex:param assignTo="{!selectedAcctId}" value="{!acct.Id}"
name="selAcctId"/>
</apex:actionSupport>
<apex:column value="{!acct.Name}">
<apex:facet name="header">Account Name</apex:facet>
</apex:column>
<apex:column value="{!acct.AnnualRevenue}">
<apex:facet name="header">Annual Revenue</apex:facet>
</apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
<apex:pageBlock id="acctDetails" title="Account Details" >
<apex:pageBlockSection columns="2">
<apex:repeat value="{!$ObjectType.Account.FieldSets.AcctSearch}" var="f">
<apex:outputField value="{!selectedAcct[f]}"/>
</apex:repeat>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>

 

public with sharing class DynamicAccountExt {
public String acctName {get;set;}
public List<Account> searchResult {get;set;}
public Account selectedAcct {get;set;}
public String selectedAcctId {get;set;}
private Map<Id,Account> acctId2Account = new Map<Id, Account>();
public DynamicAccountExt (ApexPages.StandardController controller)
{
}
public PageReference doSearch()
{
try
{
selectedAcct = null;
acctName= String.escapeSingleQuotes(acctName); //To avoid SOQL injection
acctName= acctName.endsWith('%') ? acctName: acctName+ '%';
acctName= acctName.startsWith('%') ? acctName: '%' + acctName;
acctId2Account = new Map<Id, Account> (
[select id,  OwnerId, name,
annualRevenue, BillingStreet, BillingState,
BillingPostalCode, BillingCountry,
BillingCity, ShippingStreet,
ShippingState, ShippingPostalCode,
ShippingCountry, ShippingCity
from Account
where name like :acctName
order by name limit 10]);
searchResult = acctId2Account.values();
}
catch(QueryException e)
{
ApexPages.addMessage(new
ApexPages.message(ApexPages.severity.ERROR,e.getMessage()));
return null;
}
if (searchResult == null || searchResult.size() == 0)
{
ApexPages.addMessage(new ApexPages.message(ApexPages.severity.INFO,
'No matching Account records found'));
}
return null;
}
public PageReference refreshAcctDetails()
{
selectedAcct = acctId2Account.get(selectedAcctId);
return null;
}
}

Note that in order to compile this code, you'll first have to define a Field Set (with the 'AcctSearch' API name) on the Account object that includes the Account Name, Account Owner, Billing and Shipping address fields. You can watch the webinar recoding for details on how to define a Field Set.

Lets now review the code. The controller is pretty straightforward and does not have any Field Set specific code and so lets focus on the VF markup instead. The key piece is the <pageBlock> section at the bottom of the page. This is where the Field Set is referenced with the '{!$ObjectType.Account.FieldSets.AcctSearch}' notation. Think of this as an iterator that has all the fields that the Admin has configured to be in the Field Set. The code simply iterates through that list using a <apex:repeat> tag and adds an <apex:outputField> tag for each field in the Field Set. When the user clicks on any Account record in the search result section, the bottom half of the page will refresh to display the Account fields that the Admin has included in the 'In the Field Set' list for the Field Set.  

As always, comments and questions are welcome.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS