Skip to main content

List Component

Zuora

List Component

The ListComponent provides developers with a dynamic, highly-customizable data list or grid UI component that can be used to display, edit, and select data within Salesforce. Through the use of Salesforce sObject field sets and managed package plug-ins, the developer has a full control what data is displayed and how it is handled.

The ListComponent supports the following:

  • Fields that are displayed are derived from a previously-defined field set for an sObject, enabling a drag-and-drop configuration interface for customizing the content of the component.
  • The component can be used anywhere Visualforce pages can be used.
  • Data displayed on the ListComponent can be sorted, searched, and filtered.
  • Through a plug-in architecture, developers can customize the handling of querying data, user-selected data, user-updated data, and post-update results processing.
  • Through global methods exposed from the ListComponentController, developers can customize actions such as pagination, navigation, updating changed data, and submitting selected data.
  • Using jQuery, developers can bind to UI elements and events in a structured manner to customize UI actions.
  • Through a basic HTML table structure for the data grid, developers can customize the look and feel of the table by using complete custom CSS, modifying existing CSS elements, or using jQuery to modify the CSS.
  • Developers can use the ListComponent multiple times on a single page and maintain direct control over each instance both on the client-side and server-side.
  • Developers can implement and register the List Filter Admin Plugin and enable List Filters on a custom instance of ListComponent.

The ListComponent is available in the Version 5.100 and later of Zuora Quotes.

The ListComponent consists of:

  • ListComponent.component: The Visualforce UI component
  • ListComponentController.cls: The Apex controller class for the ListComponent UI component. This class contains mechanisms for communicating with the controller class of the page that embeds the component. These mechanisms are described below
  • ListComponentOptions.cls: The Apex class that stores the configuration options specified by the developer and used by the controller to render the component
  • ListComponentJS.component: A standalone component made up of the JavaScript code required for the ListComponent
  • ListComponentCSS.component: A standalone component that contains the default CSS for the ListComponent

ListComponent Component

Component Attributes

The ListComponent has the following attributes.

Attribute  Type Description
id String An identifier that allows the component to be referenced by other components in the page

options

ListComponentOptions

The list component's configuration options.  See the ListComponentOptions Class  section that follows for option details.

rendered Boolean Specifies whether the component is rendered on the page. If not specified, this value defaults to true.

Embed ListComponent on a Visualforce Page

The following are the requirements you need to comply when embedding the ListComponent on a Visualforce page:

  • The ListComponent includes an attribute, options, which is an instance of the ListComponentOptions class that specifies the component configuration. The ListComponentOptions object must be built in an Apex class that is the controller (or extension) of the page containing the ListComponent.
  • jQuery is used for DOM manipulation and UI behaviors. To avoid conflicts with other JavaScript libraries that use the jQuery default "$" variable on the page, the ListComponent calls jQuery.noConflict() and assigns the "$jq" as the variable shortcut to the jQuery() method.
  • The component must be placed within the <apex:form> and </apex:form> tags, and any buttons or other UI elements that call the ListComponentController's global methods must be inside the same form container as the component.
  • The component is defined with the layout="none" option, so the component should be wrapped in block-level HTML elements, such as <div> or <td>
  • The data table is rendered with a basic HTML table structure, and it does not use Visualforce tags like <apex:pageBlockTable> or <apex:dataTable>. This allows for simpler overriding of the table CSS.

ListComponentOptions Class Properties

The ListComponent can be configured by constructing an instance of the ListComponentOptions class as a public property of your page controller and setting the following options.

Property Type Default Description
exceptionPlugin String The default implementation of this plugin The fully-qualified class name of the custom implementation of this plugin. If your implementation is named "MyPlugin" and is an inner class of "SomeClass", then this should be set to "SomeClass.MyPlugin".
disableSort Boolean false If set to true, the columns are not sortable.
fieldSetName String n/a

The API name of the field set that contains the fields that should be queried and rendered in the list.

The field set must belong to the sObject named in the objectName property.

Required.

hideActionButtons Boolean false

Set this to true to hide the default update buttons in the "edit" or "inlineEdit" modes or the default selection processing buttons in "singleSelect" or "multiSelect" mode so that you can implement custom buttons for these modes.

hidePaginationNav Boolean false

Set this to true to hide the default page navigation controls so that you can implement custom page navigation.

See the ListComponentController Global Methods section for instructions on implementing custom page navigation.

hideSearch Boolean  false  Set this to true to hide the search field and buttons that appear by default just above the data table. 
instanceName String  listComponent  Allows developers to give a custom name to the instance of the ListComponent. Only needs to be set when using multiple ListComponents on a singe page.

The name can include letters, underscores, and numbers, but no other characters. Also, it cannot start with a number. 
mode String detail The ListComponentController supports five modes. The mode values are case-sensitive:
  • detail - Displays the data in a table with no editing controls.
  • edit - All editable fields are displayed with the proper input controls based on the underlying field type and if the field is editable by the current user.
  • inlineEdit - Allows users to edit individual fields as long as the fields are editable by the current user. This mode requires that the containing page use the standard stylesheets. 
  • singleSelect - The leftmost column of the table will be rendered with radio buttons, allowing the user to select a single record.
  • multiSelect - The leftmost column of the table will be rendered with checboxes, allowing the user to select multiple records.

When using one of the edit,inlineEdit, singleSelect, and multiSelect modes, any selections or edits will be lost if the user moves to another page of data prior to submitting the updates or selected data.

multipleListComponents Boolean false 

Set this to true if you are using multiple instances of the ListComponent on a single page.

When setting this option to true, developers must manually embed the ListComponentJS and ListComponentCSS components on the page prior to embedding the ListComponent instances.

objectName  String  n/a

The API name of the sObject whose data will be displayed in the list.

When referencing sObjects that belong to a managed package or reside in another namespace, the namespace must be included. For example, if a managed package is part of the "MyPackage" namespace and the sObject is a custom object named "MyObject", then the value for objectName would be "MyPackage__MyObject__c".

Required.

pageSize Integer 20 The number of records to be displayed on a single page. If a value less than 1 or greater than 2000 is entered, the default number of records will be displayed.
parentController ListComponentController.
ParentController
 

When using the ListComponent on a page, the Apex controller (or extension) of that page should extend the virtual ListComponentController.ParentController class, then assign itself to this property when the options are being set. This allows all plugin implementations to be passed the current instance of the page controller so that the plugin implementations have access to all of the page controller public methods and properties (and can therefore modify what appears on the page).

Required.

postUpdatePlugin     String  The default 
implementation 
of  this plugin

The fully-qualified class name of the custom implementation of this plugin.

queryPlugin String The default 
implementation 
of  this plugin

The fully-qualified class name of the custom implementation of this plug-in.

readOnlyFields Set<String> An empty Set<String>

When in the "edit" or "inlineEdit" modes, the fields in this set will be displayed as read-only, even if the current user has the write access to the field.

The values of this set must be the fully-qualified developer name (including the namespace prefix) of the field. For example: "zqu__MyCustomField__c".

selectButtonName String Submit Selection In "singleSelect" or "multiSelect" modes, this controls the display name of the button used to submit the selected data. 
 selectionPlugin  String The default implementation of this plugin The fully-qualified class name of the custom implementation of this plugin. 
 updatePlugin  String The default implementation of this plugin The fully-qualified class name of the custom implementation of this plugin. 
updateButtonName String Update Records In the "inlineEdit" mode, this controls the display name of the button used to submit the data for updating.
useCustomCSS Boolean false  Setting this to true will suppress the default CSS used to style the list component table and buttons. See the "Using Custom CSS" section of this document for a description of the DOM element class structure and how to use custom CSS for it. 

ListComponentController Class

Access the ListComponentController Instance

In the ParentController virtual class, there is a virtual method called registerController. This method is called by the ListComponentController after it has completed its initialization. It can be overriden in your page controller to receive each instance of the ListComponentController that is on your page. The method takes both a ListComponentController and a String parameter, the value of the instanceName options. It is recommended that you store the ListComponentController instances in a map keyed by the instanceName.

ListComponetController ​Plug-Ins

The ListComponentController exposes the following interface classes that can be implemented to handle actions a user can perform with the ListComponent:

  • Query
  • Selection Handling
  • Update
  • Post-update
  • Exception

The concrete implementations of these interfaces are placed in your page controller (or any other class) and their names are passed to the ListComponentOptions object as Strings at runtime. 

In addition to other parameters, each of the plug-in methods takes an instance of ListComponentController.ParentController. When the plug-in methods are called, this parameter is passed to the parentController option from the ListComponentOptions object that your controller passed into the ListComponent. This gives your plug-in implementation access to all of the publicly-accessible methods and properties of your page controller. 

ListComponentController.IQueryPlugin

The IQueryPlugin allows a developer to modify the baseSoql then pass the modified baseSoql string to the Database.getQueryLocator(String query) method. For example, you can add a WHERE clause with additional filters to baseSoql.

This plug-in builds a QueryLocator object with the data returned by the query. The data returned by the query and passed to Database.getQueryLocator is treated as the primary data set for the ListComponent. Any searching or filtering will be performed against this primary data set.

Interface Class Signature

Database.QueryLocator getQueryLocator   (String baseSoql, ListComponentController.ParentController parentController);

The baseSoql string passed to the plugin method is in the format:

select [field1],[field2],...[fieldN] from [objectName]

where the objectName and fieldSetName are from the ListComponentOptions property values.

Default Behavior

The QueryLocator is initialized using the baseSoql string with no modifications and returned.

ListComponentController.ISelectionPlugin

The ISelectPlugin returns a PageReference, allowing the plugin implementation to send a new page to the controller user after the selected data as been processed.

When the component is in the multiSelect or singleSelect mode, use this plugin to process the selected records. 

Interface Class Signature

PageReference handleSelected   (Map<Id,SObject> selectedData, ListComponentController.ParentController parentController);

Default Behavior

The selected data is processed, and a null PageReference is returned.

ListComponentController.IUpdatePlugin

Use the IUpdatePlugin to customize the handling of data when it is submitted for updating. This could include performing additional validation or even modifying data. 

The method must call Database.update() and return the resulting List<Database.SaveResult> collection.

Only records on the current page that were modifiedare passed to this plugin.

Interface Class Signature

List<Database.SaveResult> doUpdate   (List<SObject> data, ListComponentController.ParentController parentController);

Default Behavior

The records are updated without modification or additional validation, and the "allOrNone" option passed to the Database.update() method is set to false. This allows some records to be successfully updated even if other records fail due to validation or other errors.

ListComponentController.IPostUpdatePlugin

After the data has been updated, you can use the IPostUpdatePlugin to customize handling of the List<Database.SaveResult> collection. For example, with this plugin, you can write the error messages for any failed updates to the page.

This plugin method returns a PageReference, allowing the plugin implementation to send a new page to the controller user after the update results have been processed.

Interface Class Signature

PageReference handleResults  (List<Database.SaveResult> results, ListComponentController.ParentController parentController);

Default Behavior

No additional processing is performed to the update results, and a null PageReference is returned.

ListComponentController.IExceptionPlugin

The IExceptionPlugin enables the custom gathering and display of error messages. Any validation errors or exceptions encountered after the ListComponentController is initialized will be passed to the IExceptionPlugin as an instance of ListComponentException. If the options object is null or the implementation of this plugin cannot be found, an exception is thrown directly instead of being passed to the plug-in.

Interface Class Signature

void handleException (ListComponentException lce, ListComponentController.ParentController parentController);

Default Behavior

The exception message is written to the page via ApexPages.addMessage() with a severity of ERROR.

ListComponentController Global Methods and Properties

By default, the ListComponent provides a number of UI features that can be displayed or hidden via the ListComponentOptions class properties, or optionally used in your own UI:

  • a pagination-based navigation interface for paging through records
  • a row of buttons for the "edit", "inlineEdit", "multiSelect", and "singleSelect" modes
  • a search box and buttons, rendered above the data table by default

The ListComponentController methods for these features are exposed globally so that they can be called by custom Visualforce UI controls.

Pagination Global Properties

The following properties, except goToPage, are read-only.

Property Type Description
currPage Integer   The current page number
goToPage Integer When implementing functionality to allow a user to go to a specific page, set this property to the desired page number before calling thejumpToPage() global method.
hasNext Boolean  Indicates if there is another page after the current page
hasPages Boolean  Indicates if the number of records returned in the initial query that was executed when the ListComponent was first initialized
hasPrevious Boolean  Indicates if there is another page prior to the current page
numPages Integer

The total number of pages in the current data set.

This number adjusts to reflect the correct number of pages after a search has been performed.

Pagination Global Methods

In the "edit", "inlineEdit", "multiSelect", and "singleSelect" modes, all of the following methods discards any edits or selections made by the user and re-queries the entire result set.

Method Return Type Description 
first PageReference (null) Moves to the first page
jumpToPage PageReference (null) Moves the data to the page number specified in the goToPage property
last PageReference (null) Moves to the last page
next PageReference (null) Advances to the next page of data unless the current page is the last page
previous PageReference (null) Moves to the previous page unless the current page is the first page.
Custom Pagination Sample Code
// This example assumes that there is a Visualforce page that has this class as its controller or extension, 
// and that page embeds the ListComponent.
public class MyListClass extends ListComponentController.ParentController {
  public ListComponentOptions theOptions { get; set; }
  private Map<String,ListComponentController> listControllerMap; 

  public MyListClass() {
    this.theOptions = new ListComponentOptions();
    this.theOptions.objectName = 'Account';
    this.theOptions.fieldSetName = 'ListTest';
    this.theOptions.parentController = this;
    this.theOptions.instanceName = 'MyList';
    listControllerMap = new Map<String,ListComponentController>();
  }

  public override void registerController(ListComponentController lcc, String instanceName) {
    this.listControllerMap.put(instanceName, lcc);
  }

  public PageReference nextPage() {
    ListComponentController lcc = this.listControllerMap.get(this.theOptions.instanceName);
    return lcc.next();
  }
}

Search Global Property

Property Type Description
searchStr String  Set this to the search string that a user inputs prior to calling the processSearch() global method.

Search Global Method

 Method Return Type Description
processSearch PageReference (null) Performs a search using the searchStr entered by the user.

Data Handling Global Methods

Method Return Type Description
clearResults PageReference (null) After a search has been performed, this method resets the ListComponent back to the original data sets and set searchStr to blank.
processUpdate PageReference Sends any modified records to the update and post-update plugins. Returns the PageReference object that is returned by the post-update plugin.
processSelection PageReference Sends any selected records to the selection plugin. Returns the PageReference object that is returned by the selection plugin.

Custom CSS and JavaScript

Custom CSS Notes

  • "listComponent" is the default value for the instanceName option. When using multiple list components on a page, the instanceName value you give to each one will replace "listComponent" in the markup.
  • Data rows (<tr>) in the table have an element id in the format {instanceName}:{record id}.
  • Data cells (<td>) in the table have an element id in the format {instanceName}:{record id}:{field name}.
  • In the "edit" mode, input controls inside each of the data cells are rendered automatically by Salesforce based on the underlying data type, using the apex:inputField tag. For example, text fields are rendered as <input type="text" ...>.
  • The row containing the "defaultButtonCell" is not rendered when the hideActionButtons and hidePaginationNav options are set to true.
  • The row containing the "defaultSearchCell" is not rendered when the hideSearch option is set to true.

Custom JavaScript Notes

Use the jQuery CSS selector pattern matching (http://api.jquery.com/category/selectors/) to manipulate elements, bind events to specific data cells, or bind events to groups based on the element id. For example:

  • In a ListComponent using a field set from the Account object where the AnnualRevenue field is included, to bind to all AnnualRevenue text boxes:​
    • jQuery("[id$='\\:AnnualRevenue']").children(":input[type=text]");
  • In a ListComponent using a field set from the Account object where the Industry field is included, to bind to all Industry select elements (picklists):
    • jQuery("[id$='\\:Industry']").children("select");

ListComponent Markup Structure

<div class="listComponentPanel" id="listComponent:listComponentPanel">
  <table class="outerTable" id="listComponent:outerTable">
    <tbody>
      <tr>
        <td id="listComponent:defaultSearchCell" style="text-align: right;">
          <!-- default search box and buttons, can be hidden via the list component options -->
        </td>
      </tr>

      <tr id="listComponent:dataGridRow">
        <td>
          <table class="innerTable bPageBlock" id="listComponent:innerTable">
            <thead>
              <tr>
                <th class="textField">Account ID</th>
                <th class="textField">Account Name</th>
                <th class="numericField">Annual Revenue</th>
                <th class="numericField">Created Date</th>
                <!-- additional fields -->
              </tr>
            </thead>

            <tbody>
              <tr class="dataRow" id="listComponent:001i0000008Oah1AAC">
                <td class="dataCell textField readOnly" id="listComponent:001i0000008Oah1AAC:Id"><!-- data, etc. --></td>
                <td class="dataCell textField" id="listComponent:001i0000008Oah1AAC:Name"><!-- data, etc. --></td>
                <td class="dataCell numericField" id="listComponent:001i0000008Oah1AAC:AnnualRevenue"><!-- data, etc. --></td>
                <!-- additional fields -->
              </tr>
              <!-- more data rows -->
            </tbody>

          </table>
        </td>
      </tr>

      <tr>
        <td id="listComponent:defaultButtonCell" style="text-align: center;">
          <!-- default pagination navigation and update/submit buttons, can be hidden via a ListComponent option -->
        </td>
      </tr>
    </tbody>
  </table>
</div>

Known Issues

  • When in the inlineEdit mode, there is a known issue with Visualforce inlineEdit support that prevents changes from being saved when a user enters data that causes a validation error (such as text characters into a numeric field). This issue is discussed here: http://boards.developerforce.com/t5/Visualforce-Development/Problem-with-Saving-an-Inline-Edit-that-causes-a-validation/td-p/333045This is a Visualforce defect.
  • Visualforce inlineEdit support does not support the editing of lookup fields via click events. For instance, when displaying an Account record, the Owner's name will be displayed as a link to the Owner's User record. If inlineEdit mode is set to use click or double-click as the events to enter edit mode for that field, this field cannot be edited. This is a Visualforce defect.
  • If a custom object in an org where the Zuora Quotes package is deployed has an object that has the same name as a Zuora Quotes object (except for the namespace), the Schema.getGlobalDescribe() call, which is core to the ListComponent's dynamic functionality, will only return the package object. For instance, if a Zuora customer creates an object called "Quote__c", there will be no way to access that object's properties, since calling Schema.getGlobalDescribe().get('Quote__c') will only return the Zuora Quotes package object "Quote__c". This issue was addressed in the Summer '13 release (https://success.salesforce.com/issues_view?id=a1p30000000SUwKAAW) however it has not been extensively tested here.