Saturday, 20 September 2014

Sorting Visualforce Tables with JavaScript

Overview

Since the introduction of List Sorting and SOQL offset, presenting tables of sorted data in Visualforce is straightforward.  Both of these require the data to be retrieved/sorted server side, so each time the user changes the sort order of the table a full round trip takes place, including a post containing the view state if the page contains a form. Sorting the table in the client is orders of magnitude faster, and using the JQuery tablesorter plugin, straightforward to implement.

Setup

To demonstrate this, I put together a simple custom controller that pulls 5 accounts from the Salesforce database and makes them available to the page as a list. There’s nothing particularly relevant to the topic of this post, so I won’t be digging into it.  You can view the controller at this gist.

Next, I created a Visualforce page and included the JavaScript resources.  Tablesorter can be downloaded from the home page, but I prefer to use a Content Delivery Network wherever possible. An excellent CDN for JavaScript is cdnjs.com, and this hosts all of the tablesorter resources. I’ve included the JavaScript and a theme to style the table, plus JQuery from the google CDN:

<apex:includeScript
  value="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" />>
<apex:includeScript
  value="//cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.17.8/js/jquery.tablesorter.min.js" />
<apex:stylesheet
  value="//cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.17.8/css/theme.blue.css" />

Then a simple datatable to iterate the accounts and output the details:

<apex:datatable value="{!accounts}" var="acc" id="accsTable" styleclass="tablesorter">
  <apex:column headerValue="Created">
    <apex:outputText value="{0, date, dd/MM/yyyy}">
      <apex:param value="{!acc.CreatedDate}" />
    </apex:outputText>
  </apex:column>
  <apex:column headerValue="Name">
    <apex:outputField value="{!acc.Name}" />
  </apex:column>
  <apex:column headerValue="Street">
    <apex:outputField value="{!acc.BillingStreet}" />
  </apex:column>
  <apex:column headerValue="City">
    <apex:outputField value="{!acc.BillingCity}" />
  </apex:column>
  <apex:column headerValue="State">
    <apex:outputField value="{!acc.BillingState}" />
  </apex:column>
  <apex:column headerValue="Postcode">
    <apex:outputField value="{!acc.BillingPostalCode}" />
  </apex:column>
</apex:datatable>

 Note that I’ve specified an id for the datatable and applied the tablesorter CSS class - the former allows the JavaScript to apply the sorting capability to a specific table and the latter allows the chosen theme to be applied.

 Finally, some JavaScript to turn the rendered table into a sortable table:

<script>
  $(document).ready(function()
  {
    $("[id$='accsTable']").tablesorter({theme: 'blue', dateFormat : "dd/mm/yyyy"});
  });
</script>

There are a number of configuration options for tablesorter - I’ve just used two here, to specify the theme I want to use and to change the date format to match that I have used when displaying my account created dates.  This allows tablesorter to identify the column as containing date values, and apply the appropriate sorting algorithm. The final point to note is how I’ve identified the table element - as explained in my $Components versus Selectors post, when Visualforce components are rendered the element id contains the full hierarchy of parent component identifiers, and as my data table can move around, I simply find the element that ends with my specified id, using the id$= selector.

Demo

Like my Slick Salesforce1 Messages with Alertify post, screenshots don’t really do this justice so I’ve recorded a short video showing the page in action:

You can also access a version of this page at my Force.com Demo Site

Gotchas

  • Tablesorter requires that the table contains <THEAD> and <TBODY> HTML elements. The Visualforce datatable renders these elements at present, but if Salesforce were to change this in the future the sorting capability wouldn’t be added. In this situation I’d need to convert this to a vanilla HTML table.
  • This will only sort the rows displayed on the page. If this page displayed a subset of rows in a collection, the sorted values wouldn’t reflect the full dataset. In this scenario I’d re-run the SOQL query with the appropriate ordering or sort the collection of data server side.

Resources

11 comments:

  1. My table starts off like yours but when I rerender it it does not work out for me.

    ReplyDelete
  2. This works great, thanks!
    Is there a way to set a default sort column, in order to render the page as PDF?

    ReplyDelete
  3. I found what I needed here: http://tablesorter.com/docs/#Configuration on the "sortList" row

    ReplyDelete
  4. Thanks for this solution. Its working but in my case i am using tabpanel approach, so its disabling the other tabs and when i use jquery.noconflict method its moving away with the sort. Could you please help me how to accomplish sorting in apex:tabPanel approach.

    ReplyDelete
  5. This post helps lot, however it would have been great if it was sorting the overall list of records instead of just sorting displayed rows.
    Please provide link where we can sort the whole list which we get from controller.

    ReplyDelete
  6. I am not getting the up &down arrow images . In console it shows tablesorter() is not a fucntion. I copied and pasted the same code from here. Please help.

    ReplyDelete
  7. if is used , the date sorting is not working correctly

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. Has anyone found a solution to this? I'm also having problems with sorting by date.

      Delete
  8. The date sorting is not working correctly and even the number field.

    ReplyDelete