How to Dynamically Add a Configurable List of Fields to a Visualforce Page in Salesforce

Recently while working on a project, I had a requirement to have the fields that were being displayed on a Visualforce page be data-driven. Meaning that the list had to be variable/configurable and not hard-coded. Typically this lends itself well to using a Field Set. But in this particular case there were multiple field “bundles” that would relate to each other, so a regular field set wouldn’t suffice. So I decided to go with keeping the lists in a custom setting.

The only problem with this is that while Salesforce supports putting Field Sets on a Visualforce pages dynamically, there is nothing for going rouge and storing them in a custom setting. How do you tell the page render “this” field without knowing what “this” field is at the time of coding?

Luckily I found that there is a way to bind a field on the page based on it’s name. When using the APEX InputField component you can specify the value attribute using the SObject name followed by a string in brackets, with the string specifying the field’s name.

For example, instead of:

<apex:inputField value="{!dummyOpp.StageName}" />

You can use:

<apex:inputField value="{!dummyOpp['StageName']}" />

Now the nice thing is you can use a string variable here and make it dynamic:

<apex:inputField value="{!dummyOpp[StringVarOfMyFieldName]}" />

So I was able to use a repeater with a list of field names (that I pulled from a Custom Setting) to dynamically bind those fields from the Opportunity object to the page. This way the fields being displayed are not hard coded and can be updated and maintained by an admin without updating and deploying code. This is idea for situations where the business needs are likely to change or will be determined by some other variables, and a field set just won’t do.

In the example below I use a list custom setting Opportunity_Dynamic_Field_List__c which has 4 entries in it: “Name”, “CloseDate”, “Description”, and “StageName”.  The beauty of this though that you can add more field names to this list to fit your needs without having to updated anything else.  I also included a getOpportunity function in the controller to show how to retrieve data using this methodology.

Page:

<apex:page controller="DynamicBindingController">
     <apex:pageMessages id="pageMessages" />
     <h1>These fields are speicified from a custom list</h1>
     <apex:form >
           <apex:pageBlock mode="edit">
                <apex:pageBlockButtons >
                      <apex:commandButton value="Save" action="{!createOpportunities}" />
                </apex:pageBlockButtons>
                <apex:pageBlockSection title="Dynamic fields" columns="1" showHeader="true">
                     <apex:repeat value="{!fieldNames}" var="FieldName" >
                          <apex:pageBlockSectionItem >
                               <apex:outputLabel value="{!FieldName}" />
                               <apex:inputField value="{!dummyOpp[FieldName]}" />
                          </apex:pageBlockSectionItem>
                     </apex:repeat>
               </apex:pageBlockSection>
          </apex:pageBlock>
     </apex:form>
</apex:page>

Controller:

public without sharing class DynamicBindingController {
   public Opportunity dummyOpp {get; set;} 
   public List<String> fieldNames {get; set;}

   public DynamicBindingController(){
      //Initialize the placeholder Opportunity
      dummyOpp = new Opportunity();

      //Now let's get the List of fields from our custom setting
      fieldNames = new List<String>();
      for(Opportunity_Dynamic_Field_List__c fieldName : Opportunity_Dynamic_Field_List__c.getall().values()){
         fieldNames.add(fieldName.Name);
      }
   }

   public PageReference createOpportunities(){
      try{
         //Create the new opportunity and re-direct to it
         insert dummyOpp;
         return new PageReference('/'+dummyOpp.id);
      }catch(Exception ex){
         ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.Error, ex.getMessage());
         ApexPages.addMessage(msg); 
      }
      return null;
   }

    public Opportunity getOpportunity(String id){
       String soql = 'Select id ';
       for(Opportunity_Dynamic_Field_List__c fieldName : Opportunity_Dynamic_Field_List__c.getall().values()){
          soql = soql + ', ' + fieldName.Name;
       }
       soql = soql + ' From Opportunity where id = :id';
       return (Opportunity)Database.Query(soql);
    }
}

 Rendered Page:

DynamicFields