The Silver Lining

Lessons & Learnings from a salesforce certified technical architect.

Client-Side VisualForce Pagination with Pajinate

with 9 comments

Pajinated DataTable

Pagination is an essential, and not so easy to implement user interface device that allows the developer to break long lists of items, or one very long item into sub-pages. I love the challenge that pagination brings (who doesn’t really) when developing efficient and reusable server-side code, but this article isn’t about that. Sometimes I need things done quickly, easily, and preferably with as little compromise as possible, and that’s what client-side pagination is all about.

First a shout out to some of the gurus out there, men I admire and whose server-side pagination techniques can only be described as “bloody marvelous”. If you find the need to paginate, and you want to use Apex to do it, check out these articles,

Richard Van Hook’s article on pagination using his excellent Apex library
Jeff Douglas’ article on pagination using StandardSetController
Joel Dietz’s article on pagination with Chatter Bubbles

I’ve used these articles to build server-side Paginators, but recently realised there’s another approach using jQuery plugins, one that offers the following:

Pros

  • VERY quick to implement.
  • Will work with PageBlockTables, DataTables, Repeats.
  • Will work with lists of any type, no matter how complex.
  • Will work with lists of mixed types.
  • Has nearly zero latency between paginated-pages.
  • Seriously, is VERY quick to implement.

Cons

  • We’re restricted by the number of items that can be output by PageBlockTables, DataTables, Repeats.
  • If your DOM is large, the page will load a touch more slowly.

The technology I’m using to create this pagination is a jQuery plugin I’ve created and cheesely called Pajinate. I’ve written an article on how to implement the plugin for general HTML development, but this article will demo porting the plugin so that it works with VisualForce pages. Enough yapping, let’s write some code.

I’m going to demonstrate two types of implementations, one for DataTables, and the other for Repeats. For the sake of completeness the class that serves as the page controller:

public with sharing class PajinateController {
    public List<account> accounts{get;set;}

    public PajinateController(){
        accounts = [SELECT id, name, rating, annualRevenue, industry FROM Account LIMIT 1000];
    }
}

DataTables

This is the more difficult implementation, but that doesn’t mean it’s difficult. Unfortunately Salesforce uses a strange method (I’d go so far as to say it’s bad) of automatically generating Ids for VisualForce pages, and this makes our life a smidgen more difficult.

If you peruse the code below, you’ll notice the weird way I’m including JavaScript in the middle of the page, and this is my preferred method for robustly referring to VisualForce elements in JavaScript. Secondly, on line 5 you’ll see that I’m appending a ‘:tb’ to the end of the Id we’ve assigned to the table; this is because we have no way of getting to the TBODY element that is the direct parent of the TR elements we want to page through.

            <apex:pageblock title="Client-Side Paginated DataTable">
                <apex:pageblocksection title="Pagination Easy as 1-2-3" columns="1" id="pbs">
                    <div class="page_navigation"></div>

                    <script> 
                        /* Feel free to use your favourite method of getting VF component Ids */
                        var accountTableId = '{!$Component.accountTable}'; 
                        var accountTableBodyId = accountTableId + ':tb';
                        
                        var pbsId = '{!$Component.pbs}';
                    
                    </script>

                    <apex:datatable value="{!accounts}" var="account" id="accountTable" rows="1000">
                        <apex:column>
                            <apex:facet name="header">Name</apex:facet>
                            <apex:outputfield value="{!account.name}"></apex:outputfield>
                        </apex:column>
                        <apex:column>
                            <apex:facet name="header">Rating</apex:facet>
                            <apex:outputfield value="{!account.rating}"></apex:outputfield>
                        </apex:column>
                        <apex:column>
                            <apex:facet name="header">Annual Revenue</apex:facet>
                            <apex:outputfield value="{!account.annualRevenue}"></apex:outputfield>
                        </apex:column>
                    </apex:datatable>

                    <div class="page_navigation"></div>
                </apex:pageblocksection>
            </apex:pageblock>

That’s the part of the page we need to know about, next we need to know how to attach Pajinate to the table,

        $.noConflict();

        jQuery(document).ready(function($){
            $(esc(pbsId)).pajinate({
                item_container_id : esc(accountTableBodyId),
                items_per_page : 40
            });
        });

        function esc(myid) {
           return '#' + myid.replace(/(:|\.)/g,'\\\\$1');
        }

And that’s it, done! I don’t like the little hack along the way, but it won’t keep me up at night. Right, what’s next?

Repeats

I love Repeats and they’re my favoured method of building pages using iteration (Maybe I just really don’t like tables, they’re so 2001). Attaching Pajinate to Repeats is very easy, first we build the repeat structure:

            <apex:outputpanel layout="block" styleclass="demo2">
                <div class="page_navigation"></div>
                <apex:outputtext value="Client-Side Paginated Repeat" styleclass="title block"></apex:outputtext>
                <apex:outputpanel layout="block" styleclass="content">

                    <apex:repeat value="{!accounts}" var="account" rows="50">
                        <apex:outputpanel layout="block" styleclass="panel">

                            <apex:outputtext value="{!account.name}" styleclass="head block"></apex:outputtext>

                            <apex:outputpanel layout="block" styleclass="body">
                                <apex:outputtext value="{!account.rating}" styleclass="item"></apex:outputtext>
                                <apex:outputtext value="{!account.annualRevenue}" styleclass="item"></apex:outputtext>
                                <apex:outputtext value="{!account.industry}" styleclass="item"></apex:outputtext>
                            </apex:outputpanel>

                        </apex:outputpanel>
                    </apex:repeat>

                </apex:outputpanel>

            </apex:outputpanel>

And then, in a fashion very similar to what we did previously, we use some JavaScript to attach the plugin to the required elements:


        $.noConflict();

        jQuery(document).ready(function($){
            $('.demo2').pajinate();
        });

And once again, we’re done.

Give the demos a whirl. I’m sure once you’ve used them you’ll realise that client-side pagination isn’t a second-class citizen. Note that the plugin is also configureable and the options can found on the release page. I’ll also be updating the plugin with new features and bug-fixes from time to time, so check back every now again for the latest code and documentation. For those who are interested, the full code posting can be found here.

Written by Wes

April 21, 2010 at 9:20 pm

9 Responses

Subscribe to comments with RSS.

  1. Cheesy delicious! A bit confused by the “mce”s hanging around tho.

    d3developer

    April 21, 2010 at 9:38 pm

    • That’ll be WordPress playing havok with my markup grrrr..

      Wes

      April 21, 2010 at 9:54 pm

  2. Really $#@! cool! I’m going to add this to our internal AppDEV CoE newsletter.

    Jeff Douglas

    April 22, 2010 at 12:00 pm

    • Thanks bud. I feel like I’m showing client-side a little too much love these days, time to get back to my server-side roots 😉

      Wes

      April 22, 2010 at 12:08 pm

  3. Looks great Wes.

    Steve

    January 19, 2011 at 8:12 am

  4. It seems that the code samples in this tutorial are incomplete. I don’t see the JavaScript in the middle of the Visualforce page on the datatable example, and the ids referenced in your jQuery code don’t match up with the components. I’m guessing that the JavaScript that defines “accountTableBodyId” and “pbsId” is missing from the VF page code?

    Rob

    June 8, 2011 at 8:08 pm

    • Rob, I had left that part out intentionally as I was worried that since component Ids in JavaScript are tricky that code might draw the focus away from the jQuery plug-in (the issue there is outlined here: https://th3silverlining.com/2009/06/17/visualforce-component-ids-javascript/).

      If you download the full listing at the end of the article (click on the last word I think) it is included there. That said I think that you’ve made a good point and it may be more confusing to leave it out so it’s in the main article too now. Good luck.

      Wes

      June 8, 2011 at 9:22 pm

  5. Hi,
    i have a problem with pagination.con am displaying all contact records with pagination standard set controller.
    here i put command link on contact records. my requirement is when i click on command link it will show it’s detaiiled page in visual force page.here i added one more pagination con1 clicking on next it will show next record detail page so on.
    but am comparing param id con1 got one record only .

    how can i solve my problem please anybody help me.

    Thank you.

    suresh

    August 7, 2012 at 3:27 pm

  6. I am getting this Error

    Error: Unknown page Template referenced by attribute template in in pagination at line 129 column 43

    Even if I remove apex:composition tag

    Saravanan Selvaraj

    February 21, 2014 at 6:05 pm


Leave a comment