Visualforce Page with Pagination

July 14th, 2009

Salesforce.com introduced the StandardSetController in Winter ’09 and I’m finally getting a chance to put it into use. The new pagination feature is pretty powerful and easy to use with standard as well as custom objects. Even though Jon Mountjoy has a good blog post here, there appears to be very little documentation or examples for pagination.

I threw together a small demo that allows you to page through the query results and select multiple items to process. Instead of simply returning the list of sObjects from the query locator’s current set, a collection of wrapper objects are returned enabling the user to check items for processing.

You can run this demo on my developer site.

paging1

PagingController.cls – Custom Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
 
public with sharing class PagingController {
 
	List<categoryWrapper> categories {get;set;}
 
	// instantiate the StandardSetController from a query locator
	public ApexPages.StandardSetController con {
		get {
			if(con == null) {
				con = new ApexPages.StandardSetController(Database.getQueryLocator([Select Id, Name FROM Cat3__c Order By Name limit 100]));
				// sets the number of records in each page set
				con.setPageSize(5);
			}
			return con;
		}
		set;
	}
 
	// returns a list of wrapper objects for the sObjects in the current page set
	public List<categoryWrapper> getCategories() {
		categories = new List<categoryWrapper>();
		for (Cat3__c category : (List<cat3__c>)con.getRecords())
			categories.add(new CategoryWrapper(category));
 
		return categories;
	}
 
	// displays the selected items
 	public PageReference process() {
 		for (CategoryWrapper cw : categories) {
 			if (cw.checked)
 				ApexPages.addMessage(new ApexPages.message(ApexPages.severity.INFO,cw.cat.name));
 		}
 		return null;
 	}
 
	// indicates whether there are more records after the current page set.
	public Boolean hasNext {
		get {
			return con.getHasNext();
		}
		set;
	}
 
	// indicates whether there are more records before the current page set.
	public Boolean hasPrevious {
		get {
			return con.getHasPrevious();
		}
		set;
	}
 
	// returns the page number of the current page set
	public Integer pageNumber {
		get {
			return con.getPageNumber();
		}
		set;
	}
 
	// returns the first page of records
 	public void first() {
 		con.first();
 	}
 
 	// returns the last page of records
 	public void last() {
 		con.last();
 	}
 
 	// returns the previous page of records
 	public void previous() {
 		con.previous();
 	}
 
 	// returns the next page of records
 	public void next() {
 		con.next();
 	}
 
 	// returns the PageReference of the original page, if known, or the home page.
 	public void cancel() {
 		con.cancel();
 	}
 
}

CategoryWrapper.cls – Wrapper class for the Categories

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class CategoryWrapper {
 
    public Boolean checked{ get; set; }
    public Cat3__c cat { get; set;}
 
    public CategoryWrapper(){
        cat = new Cat3__c();
        checked = false;
    }
 
    public CategoryWrapper(Cat3__c c){
        cat = c;
        checked = false;
    }
 
    public static testMethod void testMe() {
 
    	CategoryWrapper cw = new CategoryWrapper();
    	System.assertEquals(cw.checked,false);
 
    	CategoryWrapper cw2 = new CategoryWrapper(new Cat3__c(name='Test1'));
    	System.assertEquals(cw2.cat.name,'Test1');
    	System.assertEquals(cw2.checked,false);
 
    }
 
}

Category_Paging.page – Visualforce page

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<apex:page controller="PagingController">
  <apex:form >
    <apex:pageBlock title="Paging through Categories of Stuff">
 
      <apex:pageBlockButtons location="top">
        <apex:commandButton action="{!process}" value="Process Selected"/>
        <apex:commandButton action="{!cancel}" value="Cancel"/>
      </apex:pageBlockButtons>
      <apex:pageMessages />
 
      <apex:pageBlockSection title="Category Results -  Page #{!pageNumber}" columns="1">
        <apex:pageBlockTable value="{!categories}" var="c">
          <apex:column width="25px">
            <apex:inputCheckbox value="{!c.checked}"/>
          </apex:column>
          <apex:column value="{!c.cat.Name}" headerValue="Name"/>
        </apex:pageBlockTable>
      </apex:pageBlockSection>
    </apex:pageBlock>
 
    <apex:panelGrid columns="4">
    <apex:commandLink action="{!first}">First</apex:commandlink>
    <apex:commandLink action="{!previous}" rendered="{!hasPrevious}">Previous</apex:commandlink>
    <apex:commandLink action="{!next}" rendered="{!hasNext}">Next</apex:commandlink>
    <apex:commandLink action="{!last}">Last</apex:commandlink>
    </apex:panelGrid>
 
  </apex:form>
</apex:page>

Categories: Apex, Code Sample, Salesforce, Visualforce

Leave a comment

Comments Feed34 Comments

  1. Tom Patros

    Great post Jeff.

    One thing I noticed is the checkboxes don’t maintain state if you check one, go to the next page, then go back. I think it’s due to line 20 in your controller, as it recreates the List of wrapper objects with each page. You could use a second List and populate that with the checked object, then compare with each page.

    All that said, this is really a helpful post to explain the StandardSetController. I think it’s a big mystery in Apex and lacks attention. Thanks for shedding some light.

  2. jeffdonthemic

    Tom, I noticed that too but am going to save that for another post. Thanks for the comment!

  3. Prashanth

    Thanks for the post Jeff.

    I tried to use the SetController to pull up PricebookEntries from a Pricebook. I get the error “List controllers are not supported for PricebookEntry”. Is there a work around for this?

  4. jeffdonthemic

    Unfortunately there are a number of things that are not supported for objects like PricebookEntry, OpportunityLineItem, etc. You might want to post this on the Salesforce message board and possibly a product manager could give you more info.

  5. Andres Canavesi

    Im trying to do something like this:

    First Previous 1 2 3…10 Next Last

    For me that is impossible

  6. Patrick Bulacz

    No Dynamic SOQL with this though is there :(

    So I can’t exactly create a ‘search’ field on a VF page and then display paginated results…. To me this addition only gets you around having to use / create views when using a list view other than that it’s pretty useless. Correct me if I’m wrong… PLEASE…

  7. Patrick Bulacz

    Ha… Nevermind.. it works :) I’m an idiot.

  8. richardvanhook

    Nice post Jeff. I like the StandartSetController but I’ve found it inflexible in many cases so I built a concept called Paginators. Here’s an article I wrote about it: http://richardvanhook.wordpress.com/2009/08/03/visualforce-pagination-with-apex-lang/. Hope it helps.

  9. Patrick Bulacz

    Nice link Richard. I had a look at your pagination with apex-lang but couldn’t get it to work for sets larger than 1000 records. I assume a bit of tinkering would be needed to make it work for larger sets?

  10. dowithforce

    Hi nice work.
    How we can handle 10000+ records in setcontroller? is beyond its limit. Really eager to know about this.

  11. richardvanhook

    Hi Patrick, yes, the paginator code has same restriction as apex in that a list can only contain 1000 items. You could get around this via a list of lists but then of course, the paginator code would need to be modified. We need a class called BigList that would handle this for you – perhaps I’ll write one in the future!

  12. Stock

    Hi there,

    I tried out your exam, everything worked great. I tried adding partial refresh by adding rerender attribute to the commandLink tag and now its not showing the ‘Previou’ button when I go th the second page. Do you know why this is not working?

  13. Arun Kumar Pandey

    Thanks Bro it helped me

  14. Client-Side VisualForce Pagination with Pajinate « The Silver Lining

    [...] 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 [...]

  15. pk

    Is there any way to use dynamic sql with StandardSetController so that if you want to apply a keyword search similar to opprtunity line item or add to price book page.

  16. Jeff Douglas

    You should be able to do something like:

    String qry = ‘select name from account where billingCity = ‘+somecity;
    ApexPages.StandardSetController stdSetCtrl = new ApexPages.StandardSetController(Database.getQueryLocator(qry));

  17. Moavia

    Hi,
    Your post is quiet helpful. I have one issue that i cannot able to solve. It is not rerendering. When i remove the following piece of code from visualforce page, it works fine

    Visual force code:

    First
    Previous
    Next
    Last

    Controller Code:

    public with sharing class PagingController {

    public Account getAccount()
    {
    return [Select a.ParentID From Account a Limit 1];
    }

    // instantiate the StandardSetController from a query locator
    public ApexPages.StandardSetController con {
    get {
    if(con == null) {
    con = new ApexPages.StandardSetController(Database.getQueryLocator([Select Name FROM Account Order By Name limit 100]));
    // sets the number of records in each page set
    con.setPageSize(5);
    }
    return con;
    }
    set;
    }

    // returns a list of wrapper objects for the sObjects in the current page set
    public List getAccounts() {
    return (List)con.getRecords();
    }

    // indicates whether there are more records after the current page set.
    public Boolean hasNext {
    get {
    return con.getHasNext();
    }
    set;
    }

    // indicates whether there are more records before the current page set.
    public Boolean hasPrevious {
    get {
    return con.getHasPrevious();
    }
    set;
    }

    // returns the page number of the current page set
    public Integer pageNumber {
    get {
    return con.getPageNumber();
    }
    set;
    }

    // returns the first page of records
    public void first() {
    con.first();
    }

    // returns the last page of records
    public void last() {
    con.last();
    }

    // returns the previous page of records
    public void previous() {
    con.previous();
    }

    // returns the next page of records
    public void next() {
    con.next();
    }

    }

    Thanks!

  18. Jeff Douglas

    The Visualforce code didn’t make it through? Can you try again?

  19. Moavia

    Hi,

    Thanks for your reply. Can u see my post on this site:

    http://community.salesforce.com/t5/Visualforce-Development/Rerendering-problem/td-p/191481

  20. Steffen Stundzig

    Hi Jeff,

    I use your code in my sample project and it works great. Thanks for that. But If i look into the page with Platform System Admin or System Administrator account, i see the next/previous/pageNumerb links. But if i login as StandardUser next/previous and also pagenumber seems to be null. In all accounts I can see the results list.

    Do you have an idea, whats going wrong with it?

    Thanks.

  21. apex with pagination « road

    [...] http://blog.jeffdouglas.com/2009/07/14/visualforce-page-with-pagination/ [...]

  22. Shravan

    Hi Jeff,
    Is there any way that we can handle the state of check boxes for multiple pages ? I tried doing but could not get the result. Is there way to maintain second list and do it. Please let me know. Thanks !

  23. Jeff Douglas

    @Shravan, I think you would have to write onclick methods for the checkboxes that make calls to the controller methods to manage that state. I think it’s doable but alot of work. Would love to see a solution.

  24. Shravan

    Thanks Jeff for your response. I have handled it when we click on the next , previous or last methods and it worked and now i am able to handle the states of the check across multiple pages.
    Thanks!

  25. Shravan

    Hi Jeff, Now everything looks to be working fine for the account object , but when i tried the same code for the profile object i am facing an error saying “List controllers are not supported by profile”. Is that something i am missing here?
    Please let me know. Thanks a ton!

  26. indu

    Thank you for ur code.. its really work..

  27. Lakhan

    Hi Jeff, StandardSetController does not support more than 10000 records then how can we provide pagination using Apex, not client side javascript library?

    Thanks,
    Lakhan

  28. ashi

    Hi Jeff,

    How do we write test class for the main class which call the “wrapper class”? How do I mark one record as “checked” from a test class. Please help me on this.

    Thanks
    ashi

  29. Josh

    Hi Jeff,

    I’m trying to use this method of pagination, and it works great. However, I’m trying to add some filtering, by instantiating another StandardSet in a similar way to your comment here:

    You should be able to do something like:

    String qry = ‘select name from account where billingCity = ‘+somecity;
    ApexPages.StandardSetController stdSetCtrl = new ApexPages.StandardSetController(Database.getQueryLocator(qry));

    I’m able to have it generate a new “filtered” list with a modified query, but the pagination no longer works. Not sure why, and wondered if you had any input?

  30. Jimmy_Jenkins

    Jeff,
    Thanks for the example!

    Question – I had to add a call to the getCategories() method for the first(), previous(), next(), last() methods in order for the grid to update the data. The page number would update, but the data would remain the same.

    Any ideas?

  31. Ashok

    One thing I noticed is the checkboxes don’t maintain state if you check one, go to the next page, then go back. I think it’s due to line 20 in your controller, as it recreates the List of wrapper objects with each page. You could use a second List and populate that with the checked object, then compare with each page.

    CAN U PLEASE PROVIDE CODE TO resolve this
    LOGIC PLEASE

  32. ashok

    One thing I noticed is the checkboxes don’t maintain state if you check one, go to the next page, then go back. I think it’s due to line 20 in your controller, as it recreates the List of wrapper objects with each page. You could use a second List and populate that with the checked object, then compare with each page. CAN ANY ONE PLEASE GIVE ME THE CODE TO FIX THIS ISSUE,…..

  33. PB

    Does someone know how to write test class for PagingController class? Struggling a bit instantiating it as the class doesn’t have proper constructor.
    Thanks!

  34. Krishna

    Jeff,

    I have a similar issue as Jimmy_Jenkins… Can you please help?

Leave a comment

Feed

http://blog.jeffdouglas.com / Visualforce Page with Pagination

WordPress Appliance - Powered by TurnKey Linux