Using Field Sets in Spring 11

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.

SNAG_Program-0006

 

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.

tagged Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • http://www.bridgefarmconsulting.com PeterC

    Quick question – looks like the SOQL has to match the Field Set? If so, is there any way to access all the visible field from the set in the apex controller to make sure all the requested fields are queried for?
    Or should the rule be that any page using a fieldsset has to use a controller that supports that fielset?

  • http://profile.typepad.com/6p01156ff0213d970c Sandeep Bhanot

    Great question. There is unfortunately no way to access Field Sets metadata in Apex currently. Ideally this should be supported via a Apex describe call (something like Account.sObjectType.getDescribe().fieldSets().fieldsInFieldSet();). However, this is not currently supported. Therefore you do have to make sure that the SOQL query includes all the fields that could possibly be included in the Field Set.
    Keep in mind though that the above is only applicable if your VF page has some controller logic that performs a SOQL query to retrieve the SObject record. If you are using a Standard Controller like such (sorry – Typepad is stripping out the HTML formatting):
    apex:page standardController=”Account”
    apex:repeat value=”{!$ObjectType.Account.FieldSets.AcctSearch}” var=”f”
    apex:outputField value=”{!Account[f]}”/
    /apex:repeat
    /apex:page
    then you don’t have to worry about which fields are included in the Field Set. Salesforce will automatically query all the fields that are included in the Field Set for you.
    Hope this helps….

  • Bob N

    Can the FieldSet contain a currency field supported by dated exchange rates? Currently if Advanced Currency Management is enabled, you can’t include key currency fields like Opportunity Amount on a VF page. Maybe FieldSets is a way around this limitation.

  • Sandeep

    Unfortunately Field Sets will not support any feature that VF doesn’t support directly. So you won’t be able to use certain currency fields in a Field Set if ACM is enabled.
    It will let you add the field to the Field Set, but you should get a runtime error (or no value at all) on the VF page that references the currency field.

  • Chad Meyer

    We were hoping to use Field Sets as a way for customers to include their custom fields on Visualforce pages in our App. Is that possible? I question this because it looks like the ISV that creates the field set has to pre-define the specific fields customers can use.
    If this is not possible, do you know if its planned for the future?

  • http://profile.typepad.com/6p01156ff0213d970c Sandeep Bhanot

    Chad – great question. The short answer – yes. You as an ISV can define a Field Set to contain a certain ‘default’ set of fields, but customers can add custom fields that are specific to their Org. to the Field Set and have it displayed on the VF page without any code changes. In fact this is the primary use case that Field Sets was designed for.

  • http://www.3gurus.com Ajay

    Sandeep, thanks for the nice post. Would you be able to provide some sample code using the fieldset as inputfields and then using the dynamic form values, depending on the fields displayed on the VF, in Apex?

  • http://profile.typepad.com/6p01156ff0213d970c Sandeep Bhanot

    Good question Ajay. As I mentioned in one of my earlier comments in this thread (the one in reply to PeterC on Feb 3rd, 2011) there is currently no way to determine which fields are included in a Field Set in Apex (though its on the roadmap). You can’t really determine at runtime which fields are included in a Field Set in your Apex controller/extension. However, if all you want to do is update a record using Field Sets and inputField, then its very simple. Just replace the outputField tag in my sample with an inputField tag. So for e.g.
    You can then simply update the Account record in your controller/extension class (i.e. ‘update selectedAcct’) and the fields included in the Field Set will be updated. Let me know if this helps answer your question.

  • http://profile.typepad.com/6p01156ff0213d970c Sandeep Bhanot

    Sorry :( . Typepad keeps stripping out any HTML tag synatx. Here’s the VF snippet that I was referring to in my earlier comment
    apex:pageBlock id=”acctDetails” title=”Account Details”
    apex:pageBlockSection columns=”2″
    apex:repeat value=”{!$ObjectType.Account.FieldSets.AcctSearch}” var=”f”
    apex:inputField value=”{!selectedAcct[f]}”/
    /apex:repeat
    /apex:pageBlockSection
    /apex:pageBlock

  • http://www.3gurus.com Ajay

    Thanks Sandeep, that definitely answers my question. Any idea when can we have the feature of handling fieldsets in Apex? Their full potential can be realized only with Apex support. Will it be available in next release?

  • suresh

    Hi.
    For a single record set FieldSet is OK.
    I am trying to include FieldSet in a List of PageBlockTable,but unable.Is there is a good idea to do so.
    Thanks in Advance.
    cheers
    suresh

  • suresh

    Can I use FieldSet in a List
    apex:pageBlockTable value=”{!AnkenList}” var=”anken” styleClass=”detailList” id=”ankenTable”
    apex:repeat value=”{!$ObjectType.Matter__c.FieldSets.AnkenRegList}” var=”f”
    apex:column headerValue=”{!$ObjectType.Matter__c.Fields[f].label}”
    apex:inputField value=”???????????????????”
    /apex:column
    /apex:repeat
    /apex:pageBlockTable

  • http://profile.typepad.com/6p01156ff0213d970c Sandeep Bhanot

    Suresh – yes, you can absolutely do what you’re alluding to using Field Sets. You can iterate through a list of records (and not just a single record) and input/output fields on each record based on a Field Set. So in your e.g., the markup would be
    apex:pageBlockTable value=”{!AnkenList}” var=”anken” styleClass=”detailList” id=”ankenTable”
    apex:repeat value=”{!$ObjectType.Matter__c.FieldSets.AnkenRegList}” var=”f”
    apex:column headerValue=”{!$ObjectType.Matter__c.Fields[f].label}”
    apex:inputField value=”{!anken[f]}”
    /apex:column
    /apex:repeat
    /apex:pageBlockTable

  • AMITAVA

    Hi, this is great to the Custom Development. What I have a concern is that same number & same fields with Data type to be presented in
    case of FieldSets set by Admin and the Apex Controller code to get the data.
    If this is the case, there where is the dynamicity?
    And, if it is not the case, then why we are using the Code to populate Collection in the code.
    Generic would only be the case, where the FieldSet is independent of any coded development.

  • Chad Meyer

    We’re using field sets with our Apps now and we’ve run into a strange problem. If a custom picklist is included in the field set then our managed Visualforce page returns an insufficient privileges error. This page renders the field set items as apex:inputFields. Standard picklist fields and other types of custom fields all seem to work fine. An unmanaged page in the same org using the same field set works fine. Any thoughts on what could be causing this problem? Thanks.

  • Sandeep

    Chad – can you please open a support case for this issue so that our R&D team can take a closer look. Thanks
    Sandeep

  • http://www.centerstance.com Trieste LaPorte

    Looks like there is an issue with using values from the fieldset to dynamically pull values/labels in managed packages. The fieldset contains the prefix, but the dynamic expression doesn’t recognize the field with the prefix attached.

  • Chad Meyer

    Does anyone know whether this is being addressed in Summer ’11?
    This issue made Field Sets entirely unusable for us.

  • Harvey

    This is a great application for entering an account name and having details about that account displayed (results).
    I’m trying to “flip” this search to enter attributes about an account (for example, wireless vendor) and have it return a results list of all accounts that have that wireless vendor listed in that field.
    There are actually about 20 “customer environment” fields that we want to be able to search with to return a list of matching accounts, but I figure if I can get it to work for one attribute I could then build it out to work for many.
    Having a tough time figuring this out – This is my first stab at a visualforce page – wondering if you could point me in the right direction.
    Thanks!

  • http://profile.typepad.com/6p01156ff0213d970c Sandeep Bhanot

    Harvey,
    You can definitely enhance this sample to search on additional Account attributes (other than just ‘Name’). Bear in mind however that you only need Field Sets to display the search result if the Account fields that need to be displayed in the result are not ‘static’ and may need to be changed by an Admin. If you just need to display a fixed set of Account fields in the search results, you can code something much simpler that doesn’t require Field Sets. Hope this helps.

  • izumi berat

    Hi, Could you confirm that I cannot use Field Sets if the VF page I am trying to customize is pulling information from more than one object (for example, Account AND Contact).

    The current VF page I am using is retrieving information from both Opportunity and Account, I would like to customize what is displayed on this VF page using field sets based on available fields both in Opportunity and Account. I was told this was not possible. Could you please confirm?

    Thanks!

  • http://www.facebook.com/people/Assolé-Demar/100002930070508 Assolé Demar

    While you cannot directly access fieldsets within apex, you can in some instances infer enough information about what is in a field set. Take for example if you have a component on an account VF page, that needs to display values from a parent account. If in the account page, you include a block of outputText for each of the fields of the account, then if you pass the component to the component, the component can determine which fields of the account to query for the field set, but testing each field and seeing which ones do not cause an exception when accessed. Of course that means you’ll probably end-up query some extra fields you don’t really need, but in most cases that is acceptable.

  • http://www.facebook.com/people/Assolé-Demar/100002930070508 Assolé Demar

    Here is a code snippet I’m using for this type of operation:

    for(String fieldName : accountFieldMap.keySet()) {
    try {
    System.debug(‘Testing fieldName=’+fieldName);
    if(fieldName.endsWith(‘__r’)) {
    Object o = acc.get(fieldName.replace(‘__r’,'__c’));
    fieldName+=’.Name’;
    }
    else {
    Object o = acc.get(fieldName);
    if(fieldName.endsWith(‘Id’)) {
    fieldName = fieldName.replaceAll(‘Id$’,’.Name’);
    }
    }
    queryFields.add(fieldName);
    }
    catch(Exception ignored) {}
    }
    System.debug(queryFields);

    in this snippet queryFields is a Set I use later to generate a query for parent objects based on the fields that were available in the acc object above.

  • http://www.facebook.com/people/Assolé-Demar/100002930070508 Assolé Demar

    Here is a code snippet I’m using for this type of operation:

    for(String fieldName : accountFieldMap.keySet()) {
    try {
    System.debug(‘Testing fieldName=’+fieldName);
    if(fieldName.endsWith(‘__r’)) {
    Object o = acc.get(fieldName.replace(‘__r’,'__c’));
    fieldName+=’.Name’;
    }
    else {
    Object o = acc.get(fieldName);
    if(fieldName.endsWith(‘Id’)) {
    fieldName = fieldName.replaceAll(‘Id$’,’.Name’);
    }
    }
    queryFields.add(fieldName);
    }
    catch(Exception ignored) {}
    }
    System.debug(queryFields);

    in this snippet queryFields is a Set I use later to generate a query for parent objects based on the fields that were available in the acc object above.

  • http://www.facebook.com/people/Assolé-Demar/100002930070508 Assolé Demar

    Here is a code snippet I’m using for this type of operation:

    for(String fieldName : accountFieldMap.keySet()) {
    try {
    System.debug(‘Testing fieldName=’+fieldName);
    if(fieldName.endsWith(‘__r’)) {
    Object o = acc.get(fieldName.replace(‘__r’,'__c’));
    fieldName+=’.Name’;
    }
    else {
    Object o = acc.get(fieldName);
    if(fieldName.endsWith(‘Id’)) {
    fieldName = fieldName.replaceAll(‘Id$’,’.Name’);
    }
    }
    queryFields.add(fieldName);
    }
    catch(Exception ignored) {}
    }
    System.debug(queryFields);

    in this snippet queryFields is a Set I use later to generate a query for parent objects based on the fields that were available in the acc object above.

  • Vijay Raut

    Hi Sandeep,
    I read in this thread that FieldSet cannot be referred in Apex / Controller and it was on roadmap. Could you please let me know, if it is already released or on roadmap.

  • Anonymous

    Vijay – referencing FieldSets in Apex is indeed on our roadmap, but unfortunately there isn’t a specific release time frame for that yet.

  • Andrew Fawcett

    Hi Sandeep,

    RE: “As I mentioned in one of my earlier comments in this thread (the one in reply to PeterC on Feb 3rd, 2011) there is currently no way to determine which fields are included in a Field Set in Apex (though its on the roadmap).”

    Do you have an update on where on the roadmap this feature is?

    Regards,

    Andrew Fawcett

  • Anonymous

    Andrew – apologies for the delayed response. The good news is that the next release (Summer ’12) will have Apex support for Field Sets (Safe Harbor applies). You will then be able to dynamically introspect the fields in a Field Set via Apex.