Multi-tiered Tables in Visualforce

One requirement that I have received from a client was the ability to view a large set of records in a report type format with the ability to drill down into the details of the data to see where exactly the data for each row comes to as well as sorted by categories.  One such example might be having a set of invoices in a report to see how much was made over a period of time but also wanting to easily drill down into the particulars of each invoices line items without going to another page.  In this case I paired my knowledge of Salesforce development along with jQuery and the jExpand plugin.

Download jQuery and jExpand and include them in your Visualforce page:

<apex:includeScript value="{!$Resource.jQuery}" />
<apex:includeScript value="{!URLFOR($Resource.jExpand, '/jExpand/jExpand.js')}" />

Add in styling for your components

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20type%3D%22text%2Fcss%22%3E%0A%20body%20%7B%20font-family%3AArial%2C%20Helvetica%2C%20Sans-Serif%3B%20font-size%3A0.8em%3B%7D%0A%20%23originalDrilldowns%20%7B%20border-collapse%3Acollapse%3B%7D%0A%20%23originalDrilldowns%20h4%20%7B%20margin%3A0px%3B%20padding%3A0px%3B%7D%0A%20%23originalDrilldowns%20img%20%7B%20float%3Aright%3B%7D%0A%20%23originalDrilldowns%20ul%20%7B%20margin%3A10px%200%2010px%2040px%3B%20padding%3A0px%3B%7D%0A%20%23originalDrilldowns%20th%20%7B%20background%3A%237CB8E2%20url(%7B!URLFOR(%24Resource.jExpand%2C%20'%2FjExpand%2Fheader_bkg.png')%7D)%20repeat-x%20scroll%20center%20left%3B%20color%3A%23fff%3B%20padding%3A7px%2015px%3B%20text-align%3Aleft%3B%7D%0A%20%23originalDrilldowns%20td%20%7B%20background%3A%23C7DDEE%20none%20repeat-x%20scroll%20center%20left%3B%20color%3A%23000%3B%20padding%3A7px%2015px%3B%20%7D%0A%20%23originalDrilldowns%20tr.odd%20td%20%7B%20background%3A%23fff%20url(%7B!URLFOR(%24Resource.jExpand%2C%20'%2FjExpand%2Frow_bkg.png')%7D)%20repeat-x%20scroll%20center%20left%3B%20cursor%3Apointer%3B%20%7D%0A%20%23originalDrilldowns%20div.arrow%20%7B%20background%3Atransparent%20url(%7B!URLFOR(%24Resource.jExpand%2C%20'%2FjExpand%2Farrows.png')%7D)%20no-repeat%20scroll%200px%20-16px%3B%20width%3A16px%3B%20height%3A16px%3B%20display%3Ablock%3B%7D%0A%20%23originalDrilldowns%20div.up%20%7B%20background-position%3A0px%200px%3B%7D%0A%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

The next thing you need is a data structure that will play well with this.  I am pretending that we are a company who tracks Contacts as well as Opportunities connected to their client organizations.

public class ResourceWrapper
   //Week data for both the Original and Working values
   public Account customerAcc{get; set;}
   //Drilldown data
   public List<Contact> resources{get; set;}
   public List<Opportunity> opps{get; set;}
   public ResourceWrapper(Account acc)
      this.customerAcc = acc;
      resources = acc.Contacts;
      opps = acc.Opportunities;

After that, you can build your general table structure the way you want with the inner tables spanning the entire width of a column for the outer tables.  The ‘DoNothing’ class is important because it makes your inner tables rows look different than the rows on the outer table.  The ‘PleaseHideMe’ class is important because later code uses that to hide the inner rows when the page loads.

<table id="originalDrilldowns">
   <apex:repeat value="{!wrappers}" var="wrap">
<tr class="odd">
<apex:outputPanel layout="none">
<div class="arrow"></div>
<td><apex:outputField value="{!wrap.customerAcc.Name}" /></td>
<td><apex:outputField value="{!wrap.customerAcc.Industry}" /></td>
<td><apex:outputField value="{!wrap.customerAcc.Phone}" /></td>
<td><apex:outputField value="{!wrap.customerAcc.Revenue__c}" /></td>
<td><apex:outputField value="{!wrap.customerAcc.Website}" /></td>
<td><apex:outputField value="{!wrap.customerAcc.Type}" /></td>
      <apex:outputPanel layout="none">
<tr class="pleaseHideMe">
<td colspan="7">
               <apex:pageBlockTable value="{!wrap.resources}" var="drilldown" rendered="{!wrap.resources.size > 0}" rowClasses="doNothing">
                     <apex:facet name="header">Name</apex:facet>
                     <apex:outputField value="{!drilldown.Name}" />
                     <apex:facet name="header">Account</apex:facet>
                     <apex:outputField value="{!drilldown.AccountId}" />
               <apex:pageBlockTable value="{!wrap.opps}" var="drilldown" rendered="{!wrap.resources.size > 0}" rowClasses="doNothing">
                     <apex:facet name="header">Name</apex:facet>
                     <apex:outputField value="{!drilldown.Name}" />
                     <apex:facet name="header">Expected Revenue</apex:facet>
                     <apex:outputField value="{!drilldown.ExpectedRevenue}" />
                     <apex:facet name="header">CloseDate</apex:facet>
                     <apex:outputField value="{!drilldown.CloseDate}" />

Once you do this and look at your page, it will still just look like a generic looking HTML table with nothing going on. You can build this table however you want but the end product still needs to contain the correct tags. The correct tags are what gave my tables their styling and looks. The table itself needs an Id so that you can easily reference it. In my case I used #originalDrilldowns. The class named .pleaseHideMe basically is just used to collapse table rows so that you can’t see them when the page loads.

You also need the following scripts to let the table know how to act and to start off collapsed as well. The standard jQuery.noConflict() is used so that any other javascript libraries you are Salesforce use don’t interrupt your references to $. When the page loads you need:

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0A%20%20%20var%20j%24%20%3D%20jQuery.noConflict()%3B%0A%0A%20%20%20j%24(document).ready(function()%0A%20%20%20%7B%0A%20%20%20%20%20%20resetTable()%3B%0A%20%20%20%7D)%3B%0A%0A%20%20%20%2F%2FCollapses%20the%20outer%20table%20and%20hides%20the%20inner%20table.%0A%20%20%20function%20resetTable()%0A%20%20%20%7B%0A%20%20%20%20%20%20j%24(%22%23originalDrilldowns%20tr.pleaseHideMe%22).hide()%3B%0A%20%20%20%20%20%20j%24(%22%23originalDrilldowns%20tr%3Afirst-child%22).show()%3B%0A%0A%20%20%20%20%20%20j%24(%22%23originalDrilldowns%20tr.odd%22).click(function()%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20j%24(this).next(%22tr%22).toggle()%3B%0A%20%20%20%20%20%20%20%20%20j%24(this).find(%22.arrow%22).toggleClass(%22up%22)%3B%0A%20%20%20%20%20%20%7D)%3B%0A%20%20%20%7D%0A%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

The Final Result

Final results even allow multiple tables inside of the outer tables per row.

Final Results

10 thoughts on “Multi-tiered Tables in Visualforce”

    1. Damien Phillippi

      It looks like I did miss the CSS. I have added them into the page. Let me know if you need anything else!

  1. Great post. I am toiling with displaying Map data on a VF page and the only solution I’ve found uses column tags to output the field values:

    Do you know of any way to access 2 tier data using a traditional column/facet/outputtext approach? Not being able to use headers & links presents some problems.

    Many Thanks,
    Shaun McArthur

    1. Shaun, are you talking about the outer level how I am using standard HTML elements? The inner table is using the column/facet/outputtexts that you are asking about.

      Are you trying to place links directly in the header on the outer page? I’m not sure that I fully understand your needs.

  2. impressive! thanks to you i’ve had the fastest implementation of a nested toggling table ever. all i did was override the css as i needed other styles, other than that great! many thanks,

  3. Hi there,

    Can you pls share the complete code the links re broken. I’m trying to achieve the same & this would really help me achieve the use case. Thanks in advance!


  4. i am getting this error “unknown constructor ‘ResourceWrapper.ResourceWrapper()’ ”
    please heple me on this.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top