8

According to the Salesforce documentation

https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_dynamic_vf_field_sets.htm

fieldsets are a merely UI feature, although they can be accessed by Apex code too. Even the value of using them on the UI is not obvious to me, but Andrew Fawcett in his Force.com enterprise architecture mention them as an important feature:

  • What are really compelling use cases for field sets on the UI side? Are there things that you can't do without field sets or that are much more complicated?

  • Is there any value in using them when not using them in custom visualforce / lightning pages?

  • 2
    A use case that I have made use of is Field Sets in VF pages to allow an admin to change the fields included in the VF page without having to alter the code. – Dave Humm Aug 17 '17 at 12:09
  • 2
    My company is starting to use fieldsets more heavily as they can be easily changed by the admin as said. And a colleague has worked to make field sets useful in a custom lightning component as well for ease of form use. We create the component, put the fieldset component on it and then the admins can drive the fields. It is very handy for forms that change often. – JRiffe Aug 17 '17 at 13:36
8

First, as of this writing, fieldsets aren't supported in Lightning. They could potentially be created in Classic and then "applied" to Lightning situations. However, that wouldn't be easy to do. I know they're on the roadmap.

That having been said, one of the places where using them is especially helpful is when making queries. Have you ever experienced the error message "field not queried"? Field sets can help prevent that error message from occurring.

If everything on your page is included in the fieldset, you can use a service class that makes your queries which always returns the field set. If you follow that practice and don't have additional fields, you should never see that error again on a page. In that manner, you can change what's in your fieldset without ever having to be concerned with changing the details of what you've queried in your code.

| improve this answer | |
  • 2
    N.B. for users of the fflib Selector layer, fieldsets are supported as an alternative to enumerating fieldnames for either a single selector method or all selector methods – cropredy Aug 18 '17 at 21:30
6

The big thing about fieldsets is that they're declarative. If you define a fieldset, and use that in visualforce/apex, you can change the fields that are displayed in visualforce, or processed in apex, without modifying code. This also means that, in my org, I can (safely) change behavior in production without the need for a deployment.

Probably the best example I personally have of this is the custom "mass edit" tool that I developed for my org. From an Opportunity, my users can click a button that brings them to a visualforce page. There, they can select the OpportunityLineItems that they want to edit. From there, my fieldset determines what fields they are allowed to edit en masse.

If I want to change the fields available for editing, I simply add/remove things from the fieldset (my unit tests for that code also use the fieldset).

Outside of visualforce, fieldsets can also be used if you want to roll your own declarative rollup summary tool. Something like

List<String> rollupFields = new List<String>();
for(Schema.FieldSetMember f : SObjectType.OpportunityLineItem.FieldSets.OliRollupFields.getFields()) {
    // To use fields from a fieldset in a query, we need to get the field path
    //   (which returns the api name of the field)
    rollupFields.add(f.getFieldPath());
}

// String.join() allows us to easily take a list of strings, and put commas between them.
// Using String.join(), we don't need to worry about extra comma(s) at the beginning or end.
// String.join() isn't enough to put the field names in between the parenthesis
//   of 'SUM()', so we need to use replaceAll() to take care of that.
// The regex matches valid field names (alphanumeric + underscores) and stores them
//   in captured group 1.
// In the 'replace' string, $1 is substituted with the value stored in that numbered
//   captured group.
// 'a_field__c, b_field__c' would become 'SUM(a_field__c) a_field__c, SUM(b_field__c) b_field__c
String aggFunctions = String.join(rollupFields, ', ').replaceAll('([a-zA-Z0-9_]+(__c)?)', 'SUM($1) $1');
String queryString = 'SELECT OpportunityId, ' + aggFunctions + ' FROM OpportunityLineItem WHERE OpportunityId IN :oppIds GROUP BY OpportunityId';

List<Opportunity> oppsToUpdate = new List<Opportunity>();
for(AggregateResult ar :database.query(queryString)){
    Opportunity tempOpp = new Opportunity(Id = (Id)ar.get('OpportunityId'));

    for(String field :rollupFields){
        tempOpp.put(field, (Decimal)ar.get(field) == null ? 0 : (Decimal)ar.get(field));
    }

    oppsToUpdate.add(tempOpp);
}

update oppsToUpdate;
| improve this answer | |
5

Field sets are powerful because it lets one declaratively define a set of fields on an object. Each object can have multiple field sets, and at the time of this writing, there is no limit as to how many field sets an object can have.

The articles, blog posts, and other material I've come across usually present field sets in two primary use cases:

  • Form Input Fields. A form is dynamically rendered based on the fields in the field set. When defining the field set, you can also specify if the field should be required or not in addition to at the field level. Since an admin can change the fields, the fields on a record that are editable in the form can change without any Apex or Markup changes. This is really easy to implement in Visualforce since its natively supported by the framework. In Lightning, it's still doable but not as easy. See How to Use fieldsets in Lightning? for a good reference on how to get started.

  • Record Output. Dynamically showing which fields to display from a record or records. One feature that is often overlooked is that you can traverse up one level and include fields from parent records. The limitation here is that you can only go up one level instead of the usual 5 levels you can with SOQL. One workaround to this is to create a formula field to grab fields higher up and then include the formula field in the field set.

Other Use Cases

  • Custom Cloning of Records. Sometimes there's a need to clone a record using custom logic and the set of fields to clone from the source record to the cloned record need to be more granularly controlled. Using a field set, one can define which fields are cloned from the source record to the cloned record.
  • Section level control. A form or record output may have multiple sections to it. Each section has one or more fields for input or display. A field set can be used for each section giving one more control over the fields in that section of the form.

Limitations

  • In apex, there's no method to grab the field's help text using the Field Set Member's Functions. In Visualforce, this is not a big deal, usually, because an inputField is most often used and it takes care of rendering the helptext for the referenced field. The solution is to grab the FieldDescribeResult for that field and grab the help text from it and use it as needed.
  • Can only include fields from the object or from one level up. See more details in the Record Output primary use case above.
  • Can't add custom attributes. There are times when you want to provide additional field attributes to drive the behavior of something but that's not possible. For example, this field should have this custom label on this particular form only. One workaround is to define this metadata elsewhere and combine the metadata at runtime to implement the desired behavior.
  • No native Lightning component support. Since Lockerservice prevents one from doing {! record[field] }, like you would in Visualforce, it takes a bit more effort to dynamically bind a field from a record from a field set. The high-level process is 1) Create one or more custom apex wrapper objects to store the field info. 2) Pass the fields back to the lightning component along with the record(s). 3) Through Javascript, use the component's getReference to dynamically reference a field on a record such as component.getReference('v.record.' + fieldName); See my Lightning Component Dev Tips 3 blog post for more info.
| improve this answer | |
  • +1 for the custom cloning idea (can also be used in the 'negative' - fieldset to define fields that shouldn't be cloned - like fields that might be external Ids) – cropredy Aug 20 '17 at 18:58
  • @cropredy Good point. They can be used to exclude fields too. – Luke Freeland Aug 21 '17 at 4:37
3

Here's another use case for fieldsets - the Selector layer

Note: This example presumes awareness of the fflib methods for the Selector Layer but the principles apply if you are rolling your own Selector layer

For a given selector, let's say on Contact, you might have these two overloaded methods in ContactsSelector

public Contact[] selectByAccountId(Set<Id> accountIds) {
  return selectByAccountId(accountIds,false);
}

public Contact[] selectByAccountId(Set<Id> accountIds, Boolean isViewStateOptimized) {
    if (accountIds.isEmpty() return new List<Contact>();

    fflib_QueryFactory contactQF = newQueryFactory(false)
      .selectFields(isViewStateOptimized
        ? SObjectType.Contact.Fieldsets.ViewStateOptimizedFieldSet
        : SObjectType.Contact.FieldSets.AllFieldsFieldSet)
      .setCondition('Contact.AccountId IN: accountIds');

    return Database.query(contactQF.toSOQL());

}

You use fieldsets to define the fields to be returned in the query.

  • Your VF controller calls ContactsSelector.newInstance().selectByAccountIds(someAccountIds,true); This might help avoid viewstate issues on Accounts with many Contacts.
  • Other parts of your application that are insensitive to ViewState concerns might call ContactsSelector.newInstance().selectByAccountIds(someAccountIds);
| improve this answer | |
  • Thanks, we are using fflib. FieldSets are available in apex using SObjectName.FieldSetName ? – Edmondo1984 Apr 18 '19 at 17:10
  • 1
    oops - my bad - fixed code above – cropredy Apr 18 '19 at 18:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.