Friday, June 24, 2011

SalesForce: Work Around to Overcome Collection Size Limit of 1000 elements.

SalesForce seems to be a wonderful tool for users but for Apex and Visual Force developers, it sometimes becomes a nightmare to encounter the powerful attack of its ‘Limits’ or 'Governor Limits'. Here, I tried to find a solution for one such attack that you might encounter during your development and it would say something like this:

"Collection size 1,504 exceeds maximum size of 1,000."

This could be a powerful punch that can leave you with a bleeding nose, if you designed your VF page with select lists and are now trying to see the results. The error comes when you want to show more than 1000 elements in a Select List on a Visual Force Page.

After a lot of deliberation, I am able to find out a work-around for this problem and here is what I did.
All the items are added in one list but while displaying it on VF page; I divided them into multiple lists of 1000 elements. I am yet to find a way to pass parameter to the function at the time of loading VF page. So, I created 20 functions to get 20 lists to show up to 20,000 elements in my select list. You can add more functions to show more elements.

Just one word of caution while applying this solution and that is, to make sure not to exceed the Apex Script Statement limit. In below example, you can reduce number of Apex statements by exposing ‘selectList’ as public from PaginatedSelectList class and adding elements directly to it.

PaginatedSelectList:
public with sharing class PaginatedSelectList {
      private List<SelectOption> selectList=new List<SelectOption>();
      private final Integer SUB_LIST_SIZE=1000;
      //Returns total numbers of items.
      public Integer getSize(){
            return selectList.size();
      }
      //Add SelectOption.
      public void add(SelectOption so){
                  selectList.add(so);
      }
      //Gets SelectOption at given index.
      public SelectOption get(Integer index){
            if(index>-1 && index<getSize()){
                  return selectList.get(index);
            }else{
                  return null;
            }    
      }
      //Removes SelectOption at given index
      public void remove(Integer index){
            if(index>-1 && index<getSize()){
                  selectList.remove(index);
            }    
      }

      public void clear(){
            selectList.clear();
      }

      public List<SelectOption> getSelectList(){
            return selectList;
      }

      //Gets Select Options for the given start and end index both are inclusive.
      public List<SelectOption> getSubList(Integer startIndex,Integer endIndex){
            List<SelectOption> subList=new List<SelectOption>();
            if(startIndex>-1 && startIndex<selectList.size()&& endIndex>-1 && endIndex<selectList.size()&&startIndex<=endIndex){
                  for(Integer i=startIndex;i<=endIndex;i++){
                        subList.add(get(i));
                  }
            }
            return subList;
      }
      //Gets Nth sub list by dividing the main list in sublists of SUB_LIST_SIZE(1000 elements).
      public List<SelectOption>getNthSubList(Integer index){
            Integer subListCount=(Integer)(selectList.size()/SUB_LIST_SIZE);
            if(Math.mod(selectList.size(),SUB_LIST_SIZE)>0) subListCount++;
            if(index>=0 && index<=subListCount){
                  Integer startIndex=index*SUB_LIST_SIZE;
                  Integer endIndex=index*SUB_LIST_SIZE+(SUB_LIST_SIZE-1);
                  if(endIndex>=selectList.size())endIndex=selectList.size()-1;
                  return getSubList(startIndex,endIndex);
            }else{
                  return new List<SelectOption>();
            }
      }
      //Gets sublist for given index.

      public List<SelectOption>get0(){
            return getNthSubList(0);
      }
      public List<SelectOption>get1(){
            return getNthSubList(1);
      }
      public List<SelectOption>get2(){
            return getNthSubList(2);
      }
      public List<SelectOption>get3(){
            return getNthSubList(3);
      }
      public List<SelectOption>get4(){
            return getNthSubList(4);
      }
      public List<SelectOption>get5(){
            return getNthSubList(5);
      }
      public List<SelectOption>get6(){
            return getNthSubList(6);
      }
      public List<SelectOption>get7(){
            return getNthSubList(7);
      }
      public List<SelectOption>get8(){
            return getNthSubList(8);
      }
      public List<SelectOption>get9(){
            return getNthSubList(9);
      }
      public List<SelectOption>get10(){
            return getNthSubList(10);
      }
      public List<SelectOption>get11(){
            return getNthSubList(11);
      }
      public List<SelectOption>get12(){
            return getNthSubList(12);
      }
      public List<SelectOption>get13(){
            return getNthSubList(13);
      }
      public List<SelectOption>get14(){
            return getNthSubList(14);
      }
      public List<SelectOption>get15(){
            return getNthSubList(15);
      }
      public List<SelectOption>get16(){
            return getNthSubList(16);
      }
      public List<SelectOption>get17(){
            return getNthSubList(17);
      }
      public List<SelectOption>get18(){
            return getNthSubList(18);
      }
      public List<SelectOption>get19(){
            return getNthSubList(19);
      }
}

Controller:
public with sharing class SelectListController {
      public SelectListController(){
            TestValue=2011;
      }
      public PaginatedSelectList TestList{
        get{
            if(TestList==null){
                  TestList=new PaginatedSelectList();
                  for(Integer i=1;i<=20000;i++){
                        TestList.add(new SelectOption(i+'',i+''));
                  }
            }
            return TestList;
        }
        set;
    }
    public Integer TestValue{
      get;
      set;
    }
}

Visual Force Page:
<apex:page controller="SelectListController" >
      <apex:form >
            <apex:selectList value="{!TestValue}" size="1" >
           <apex:selectOptions value="{!TestList.0}"/>
           <apex:selectOptions value="{!TestList.1}"/>
           <apex:selectOptions value="{!TestList.2}"/>
           <apex:selectOptions value="{!TestList.3}"/>
           <apex:selectOptions value="{!TestList.4}"/>
           <apex:selectOptions value="{!TestList.5}"/>
           <apex:selectOptions value="{!TestList.6}"/>
           <apex:selectOptions value="{!TestList.7}"/>
           <apex:selectOptions value="{!TestList.8}"/>
           <apex:selectOptions value="{!TestList.9}"/>
           <apex:selectOptions value="{!TestList.11}"/>
           <apex:selectOptions value="{!TestList.12}"/>
           <apex:selectOptions value="{!TestList.13}"/>
           <apex:selectOptions value="{!TestList.14}"/>
           <apex:selectOptions value="{!TestList.15}"/>
           <apex:selectOptions value="{!TestList.16}"/>
           <apex:selectOptions value="{!TestList.17}"/>
           <apex:selectOptions value="{!TestList.18}"/>
           <apex:selectOptions value="{!TestList.19}"/>
           <apex:actionSupport event="onchange" rerender="testValue"/>
            </apex:selectList>
            &nbsp;&nbsp;<apex:outputText id="testValue" value="{!TestValue}"/>
      </apex:form>
</apex:page>