Wednesday, October 10, 2012

Pagination with maintaing the state of selected records

Hi All,

I have seen many people implemented pagination using salesforce, but recently I have come across a situation where I have to select list of records using pagination and process the records which are selected.

If use the StandardSetController, the check-boxes don’t maintain state if you check one and then go to the next page.
To solve this problem I have implemented custom Iterator.

You can run this demo in my developer org 



First Class Code:
global class CustomIterable implements Iterator<list<AccountInner>>{

   List<Account> accs {get; set;}
   list<AccountInner> accInnerList {get; set;}
   list<AccountInner> accInnerListRequested {get; set;}
   AccountInner accInnerObj;
   Integer i {get; set;}
   public Integer setPageSize {get; set;}

   public CustomIterable(string sQuery){
       //accs = [SELECT Id, Name, NumberOfEmployees FROM Account Limit 36];
       accs = Database.Query(sQuery);
       accInnerList = new list<AccountInner>();
       accInnerListRequested = new list<AccountInner>();    
       for(Account a : accs) {
            accInnerObj = new AccountInner(false, a);
            accInnerList.add(accInnerObj);
       }
       setPageSize = 10;
       i = 0;
   }  

   global boolean hasNext(){
       if(i >= accInnerList.size()) {
           return false;
       } else {
           return true;
       }
   }
  
   global boolean hasPrevious(){
       system.debug('I am in hasPrevious' + i);
       if(i <= setPageSize) {
           return false;
       } else {
           return true;
       }
   }  

   global list<AccountInner> next(){      
       system.debug('i value is ' + i);
       accInnerListRequested = new list<AccountInner>();
       integer startNumber;
       integer size = accInnerList.size();
       if(hasNext())
       { 
           if(size <= (i + setPageSize))
           {
               startNumber = i;
               i = size;
           }
           else
           {
               i = (i + setPageSize);
               startNumber = (i - setPageSize);
           }
          
           system.debug('i value is =====' + i);
           system.debug('i value is 2==== ' + (i - setPageSize));
          
           for(integer start = startNumber; start < i; start++)
           {
               accInnerListRequested.add(accInnerList[start]);
           }
       }
       return accInnerListRequested;
   }
  
   global list<AccountInner> previous(){     
       accInnerListRequested = new list<AccountInner>();
       system.debug('i value is previous before =====' + i);
       integer size = accInnerList.size();
       if(i == size)
       {
           if(math.mod(size, setPageSize) > 0)
           {   
               i = size - math.mod(size, setPageSize);
           }
           else
           {
               i = (size - setPageSize);
           }
       }
       else
       {
           i = (i - setPageSize);
       }
      
       system.debug('i value is previous =====' + i);
       system.debug('i value is 2previous ==== ' + (i - setPageSize));
      
       for(integer start = (i - setPageSize); start < i; ++start)
       {
           accInnerListRequested.add(accInnerList[start]);
       }
       return accInnerListRequested;
   }  
}

Second Class:
global Class AccountInner
{
    public boolean isSelected {get;set;}
    public Account acc {get;set;}
   
    public AccountInner(boolean isSelected, Account acc)
    {
        this.isSelected = isSelected;
        this.acc = acc;
    }
}

Visual force page :
<apex:page controller="Example1">
  <apex:form >
      <apex:sectionHeader title="Pagination" subtitle="Hi"/>
      <apex:pageBlock >
          <apex:pageBlockSection >
              <apex:pageBlockTable value="{!accInnerObj}" var="inner">
                  <apex:column >
                      <apex:inputCheckbox value="{!inner.isSelected}"/>
                  </apex:column>
                  <apex:column headerValue="Account">
                      <apex:outputText value="{!inner.acc.Name}" />
                  </apex:column>   
              </apex:pageBlockTable>
          </apex:pageBlockSection>
      </apex:pageBlock>
     
      <br/>
      <apex:commandButton value="<<Previous" action="{!previous}" rendered="{!hasPrevious}"/>
      <apex:commandButton value="Next >>" action="{!next}" rendered="{!hasNext}"/>
     
  </apex:form>
</apex:page>

Corresponding Controller:
public class Example1 {
   
    CustomIterable obj;
    public list<AccountInner> accInnerObj {get;set;}
   
    public Example1 () {      
        string sQuery = 'SELECT Id, Name, NumberOfEmployees FROM Account Limit 36';
        obj = new CustomIterable(sQuery);
        obj.setPageSize = 10;
        next();
    }
   
    public Boolean hasNext {
        get {
            return obj.hasNext();
        }
        set;
    }
   
    public Boolean hasPrevious {
        get {
            return obj.hasPrevious();
        }
        set;
    }
   
    public void next() {
        accInnerObj = obj.next();
    }
   
    public void previous() {
        accInnerObj = obj.previous();
    }
}

16 comments:

  1. This was extremely useful! Thanks for sharing this!

    ReplyDelete
  2. Hi Laxman,

    Thanks you for posting the code, Although I have hit a issue on this, i.e. If is multi select a couple account on page 1 then select a few on page 2 then go back page 1 and deselect one of the selected ones, the deselected one still appears on the result.

    Can you please provide me with your inputs on this.

    Thanks
    Vishant

    ReplyDelete
  3. I forgot to mention i added the method below to display what was selected, it shows the result on click of a button on thr VF page.

    public void showSelected(){

    List lists= new list();
    for (AccountInner a : obj.accInnerList){
    if (a.IsSelected == true){
    lists.add(a);
    }
    }

    accInnerObjSelected = lists;
    }

    ReplyDelete
  4. I am giving this a try but even thought the checkboxes are maintained throughout every page, the records are not. So if I hit save, only the records on the last page I selected are saved. Any idea what I could be missing?

    ReplyDelete
  5. I Tried this method removing limit from SOQL query it gives me error

    ReplyDelete
  6. Hi Laxman ,i need pagination in the form of " << < 1 2 3 4 > >> " if i select a page that page should be highlighted ,can you please help me out , Here << = first page >> = last page ,> = next,< = previous ,

    ReplyDelete
  7. You are my man !!!!
    I passed 1 week on this shit before founding your solution !!

    ReplyDelete
  8. what is the pagination limit for using iterator? like for using offset is 2000, standardsetcontroller is 10000.

    ReplyDelete
  9. Hi.. I have add/ remove rows how to maintain state and pagination

    ReplyDelete
  10. Awesome post!
    Has anyone implemented first and last button functionalities too using this pagination technique?

    ReplyDelete
  11. please give me this answer for salesforce lightning

    ReplyDelete
    Replies
    1. Below link would work for both Salesforce classic and lightning

      http://www.sfdcpoint.com/salesforce/pagination-using-standardsetcontroller-with-wrapper-class/

      Delete
  12. hi laxman can you share me the code how to write on lead and contact objects wapper and pagienation also................

    ReplyDelete
  13. Hi Laxman,
    How can you maintain pagination when u use Offset in controller class instead of using standard controller

    ReplyDelete