Saturday, April 1, 2017

Keyword Search Series : Custom Controller Pagination ~ easiest way

Motivation

So far we know, pagination can be easily be achieved by StandardSetController class.

I have seen cases where developer has a notion like until and unless we use controller extension and StandardSetController as an argument in Constructor, pagination is difficult to achieve. For this, I have tried this approach in Custom Controller with the search input from Visualforce screen.

Use Case


User will provide the input and clicking on Search button, the results will be displayed in paginated format. User can change the page size and system will fetch the records based on selected page size.

Solution

For the sake of learning, I have created a visualforce page as below.


Visualforce

It will take Opportunity Name as input search criteria and clicking on Search button it will pass to the 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
<apex:page id="filterPage" controller = "FilterSearch">
 <apex:form style="overflow-y:auto;overflow-x:auto;margin-right: 5px;margin-left: 5px;margin-top: 3px;" id="searchForm">
        <apex:actionFunction name="refreshPageSize" action="{!refreshPageSize}" status="fetchStatus" reRender="searchBlock"/>
        
  <apex:outputLabel value="Search By Opportunity Name:"/>          
        <apex:inputText value="{!searchString}" label="Opportunity Name:"/>            
        <apex:commandButton value="Search " action="{!showAll}"/>
  
        <apex:pageBlock title="Results" rendered="{!showResult}" id="searchBlock"> 
            <apex:pageblockTable value="{!Opps}" var="a" id="tabId">  
                <apex:column>
     <a href="/{!a.id}" target="_blank"> {!a.name}</a>
    </apex:column>
    <apex:column rendered="false" id="id">
     <apex:outputField value="{!a.id}" />
    </apex:column>
    <apex:column headerValue="Account Name">
     <apex:outputField value="{!a.account.name}" />
    </apex:column>

    <apex:column headerValue="Stage">
     <apex:outputField value="{!a.stagename}" />
    </apex:column>

    <apex:column headerValue="Forecast Category" >
     <apex:outputField value="{!a.ForecastCategoryName}"  />
    </apex:column>

            </apex:pageBlockTable>
            <apex:panelGrid columns="8">             
                <apex:selectList value="{!size}" multiselect="false" size="1" onchange="refreshPageSize();">
                    <apex:selectOptions value="{!paginationSizeOptions}"/>
                </apex:selectList>           
                <apex:commandButton status="fetchStatus" reRender="searchBlock" value="<<" action="{!setCon.first}" disabled="{!!setCon.hasPrevious}" title="First Page"/>
                <apex:commandButton status="fetchStatus" reRender="searchBlock" value="<" action="{!setCon.previous}" disabled="{!!setCon.hasPrevious}" title="Previous Page"/>
                <apex:commandButton status="fetchStatus" reRender="searchBlock" value=">" action="{!setCon.next}" disabled="{!!setCon.hasNext}" title="Next Page"/>
                <apex:commandButton status="fetchStatus" reRender="searchBlock" value=">>" action="{!setCon.last}" disabled="{!!setCon.hasNext}" title="Last Page"/>
                <apex:outputText >{!(setCon.pageNumber * size)+1-size}-{!IF((setCon.pageNumber * size)>noOfRecords, noOfRecords,
                     (setCon.pageNumber * size))} of {!noOfRecords}
                </apex:outputText>                 
                <apex:outputPanel >                      
                </apex:outputPanel>
            </apex:panelGrid>                                      
      </apex:pageBlock> 
    </apex:form>    
</apex:page>


Custom Controller

It has following activities:

1. In the Constructor, assigning default page size and preparing the list of page size options.

2. showAll() - This method is getting called with Search button is clicked. In this method, searchString is getting collected and getting passed into dynamic query.

Now, main important part, this line, create QueryLocator object using Database.getQueryLocator(query) and assign that into ApexPages.StandardSetController constructor to create StandardSetController object.

Rest is easy. StandardSetController will take all burden for pagination.


setCon = new ApexPages.StandardSetController(Database.getQueryLocator(query));


3. getOpps() - This method return List of records based on pageSize.

4. refreshPageSize() - This method is getting called when paginated size is changed on screen.


 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
public class FilterSearch  
{
    public String searchString{get;set;}
    public Boolean showResult{get;set;}
    public Integer noOfRecords{get; set;}
    public Integer size{get;set;}
    public ApexPages.StandardSetController setCon{get;set;}    
    public List<SelectOption> paginationSizeOptions{get;set;}
 
    String errorStr = '';    
         
    /**
    * FilterSearch
    * Constructor to initiate the default values at the time of Loading
    */    
    public FilterSearch()
    {
        size=10; //default page size

  //pagination size options
        paginationSizeOptions = new List<SelectOption>();
        paginationSizeOptions.add(new SelectOption('10','10'));
        paginationSizeOptions.add(new SelectOption('20','20'));
        paginationSizeOptions.add(new SelectOption('30','30'));
              
    }
    
    public void showAll()
    {
        try
        {
            if(String.isNotBlank(searchString))
            {  
              //build SOQL query string
             String query='Select Id,Name, account.name, stagename, ForecastCategoryName FROM Opportunity WHERE Name LIKE \'%' + searchString + '%\' order by Name LIMIT 1000';
    
               //return querylocator to an instance of StandardSetController
                setCon = new ApexPages.StandardSetController(Database.getQueryLocator(query));            
                if(setCon.getResultSize() >0)
                {
                    showResult = true;
                }else{
                    showResult = false;
                    ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'No Records Found.'));
                }        
            }            
        }catch(Exception ex)
        {
            errorStr ='Error Occured while Searching.';
            ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR, 'errorStr:' +ex.getMessage()));
        }   
    }

    public List<Opportunity> getOpps() 
    {
        if(setCon.getResultSize() >0)
        {
          setCon.setPageSize(size);  
          noOfRecords = setCon.getResultSize();
          return (List<Opportunity>)setCon.getRecords();
        }else
        {
          return null;
        }
        return null;
    }
      
    /**
    * refreshPageSize
    * Changes the size of Pagination.
    * @param    
    * @return void
    */
    public void refreshPageSize() 
    {
        setCon.setPageSize(size);         
    }
}

Results



Hope it helps! and if so, then share with your friends.

Link to my answer in stackexchange: My Answer in Stackechange

Further Reading