function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Jigar ShahJigar Shah 

Issue with Datatable in Visualforce..

I am trying to perform some processing when the user clicks on a button after performing multiple selections in list box. Once the button is clicked the selections are divided into a batch of 25 elements each and stored in Map and the action poller is enabled.

The poller picks up the records and processes one batch at a time and returns the results which are being stored in a List<CustomType>. The List<CustomType> is bound to a <apex:dataTable> to display the output.

 

The Problem:

In case of multiple batches each batch is processed and the List<MyCustomType> is populated correctly with elements from all batches. However when the table is finally rendered, values from only the last batch that was processed are displayed within the table.

 

The VF code is as follows:

<apex:page controller="MyCtl" action="{!getValuesToSelect}">
<apex:form>
<apex:outputPanel id="pnlInput">
<apex:pageBlock>
<apex:pageBlockButtons location="bottom">
<apex:commandButton action="{!createBatches}" value="Display"/>
</apex:pageBlockButtons>

<apex:actionPoller id="poller" action="{!processBatch}" enabled=" {!IsPollerEnabled}" rerender="pnlDisplayAllFieldDetails" interval="5" status="tableStatus"/>

</apex:form>

<apex:outputPanel id="pnlDisplayAllFieldDetails">
<apex:dataTable value="{!MyCustomList}" var="ft" rendered="{!IF(IsFieldTableVisible == True, True, False)}">

<apex:column headerValue="FIELD 1">
<apex:outputText value="{!ft.Field1}"/>
</apex:column>

<apex:column headerValue="FIELD 2">
<apex:outputText value="{!ft.Field2}"/>
</apex:column>
</apex:dataTable>
</apex:outputPanel>

</apex:page>

 

The Apex Controller code is as follows:

 

public class MyCtl{

public List<MyCustomType> MyCustomList = new List<MyCustomType>();

public MyCtl()
{}

public class MyCustomType{

public MyCustomType(String pField1, String pField2)
{
Field1 = pField1;
Field2 = pField2;
}

public String Field1{get; set;}
public String Field2{get; set;}
}

public List<MyCustomType> getMyCustomList()
{ return MyCustomList; }

public void processBatches()
{
/* perform some processing */
MyCustomList.add(new MyCustomType(strField1, strField2));
}

public void createBatches()
{
/*
Iterate over the selection
Create batch of 25 elements each of List<String>
Store the list in Map<Integer, List<String>> BatchMap = new Map<Integer, List<String>>
*/
}

}//End

 

  Can someone please suggest what can be possibly wrong in my code !!

 

 

Best Answer chosen by Admin (Salesforce Developers) 
Jigar ShahJigar Shah

Hi Bob,

 

Thanks for replying. Finally I managed to figure out the issue.

 

The issue was that I would add the values from IntermediateList to the DisplayList, list that was bound with the datatable on UI using DisplayList.addAll(IntermediateList). Then I would clear the IntermediateList.

 

Solution:

What happens is that the DisplayList would still hold the reference of IntermediateList and thus would contain no elements when the IntermediateList was cleared i.e. in Apex, a Shallow Copy of the collection is created when we say List.addAll(ListName) . Hence instead of clearing the list everytime, I created a new reference for the intermediate List.

 

I think the Controller code below would explain it better.

 

public class ProcessData{

//List that is bound to the dataTable on UI
List<MyCustomType> DisplayList = new List<MyCustomType>(); //List that holds the ouput data after each batch is processed
List<MyCustomType> IntermediateList = new List<MyCustomType>(); public void processSelectedObjects() { /* Process selected values and populate the IntermediateList */ } private void updateMapIndex() { DisplayList.addAll(IntermediateList); IntermediateList.clear(); //Code causing the irregular behaviour IntermediateList = new List<MyCustomType> //Correct Code } }

 

Appreciate all the help, Bob. Cheers !!

All Answers

bob_buzzardbob_buzzard

Usually when this sort of thing happens it means that you are re-using the same list instance rather than creating a new one each time.  Thus each iteration overwrites the previous one.

 

Are you re-initialising a list by doing list.clear() 

Jigar ShahJigar Shah

Thanks for the response Bob..

 

What I am doing is, getting output for each batch in a intermediate list and adding all its elements to the List<MyCustomType> MyCustomList. And then I clear the intermediate list. Also I have checked that, MyCustomList contains all the elements, but only issue is, it displays the last batch processed, in the datatable.

 

Does this problem occur because I am rerendering the outputpanel which contains the datatable using the action poller?

Would putting an extra outputPanel within pnlDisplayAllFieldDetails which would then contain the datatable, help?

bob_buzzardbob_buzzard
Its difficult to tell as I can't see your real code. Can you post the actual methods rather than comments?
Jigar ShahJigar Shah

Please find the detailed code below:

 

VF Code:

 

<apex:page controller="MyCtl" action="{!getAllItemsToSelect}">
   
   <apex:pageMessages />
   
   <apex:form>
   	<apex:outputPanel id="pnlInput">
        <apex:pageBlock >
            <apex:pageBlockButtons location="bottom">
        	<apex:commandButton action="{!processSelection}" value="Display" rerender="pnlPoller"/>
            </apex:pageBlockButtons>
        	
            <apex:outputLabel value="Select Item:"/>
            <apex:selectList id="selectObject" value="{!SelectedNameList}" multiselect="true">
                <apex:selectOptions value="{!ItemList}"/>
            </apex:selectList>
            
            <p>
            <apex:outputLabel value="Select Category:" for="rdCategory"/>
   	    <apex:selectRadio id="rdCategory" value="{!SelectedCategory}">
      		<apex:selectOptions value="{!CategoryLblList}"/>
      	    </apex:selectRadio></p>
        </apex:pageBlock>
      </apex:outputPanel>
      
      <apex:outputPanel id="pnlPoller">
      		<apex:actionPoller action="{!getAllFieldValues}" enabled="{!IsPollerEnabled}" rerender="pnlDisplayAllFieldDetails" interval="5" status="tableStatus"/>
      </apex:outputPanel>
      
   </apex:form>
   
   <div align="center">
   	  <apex:actionStatus id="tableStatus">
	    <apex:facet name="start" >                
		<center> PROCESSING...</center>                
	    </apex:facet> 
	    <apex:facet name="stop"/>            
	  </apex:actionStatus>
   </div>
   
   <apex:outputPanel id="pnlDisplayAllDetails">
       <apex:dataTable id="tblDisplay" value="{!DisplayItemResultList}" var="ft" rendered="{!IF(IsTableVisible == True, True, False)}">    
            <apex:column headerValue="FIELD1">
                <apex:outputText value="{!ft.Field1}"/>
            </apex:column>
             
            <apex:column headerValue="FIELD2">
                <apex:outputText value="{!ft.Field2}"/>
            </apex:column>
       </apex:dataTable>
   </apex:outputPanel>
   
</apex:page>

 

 Controller Code:

 

public class MyCtl
{								 
  private List<Selectoption> ItemList= new List<Selectoption>();			    private List<Selectoption> CategoryLblList = new List<Selectoption>();
 private String SelectedCategory = 'all'; private static String[] SelectedNameList = new String[]{}; private static List<MyCustomType> ItemResultList = new List<MyCustomType>();
private static List<MyCustomType> DisplayItemResultList; private Map<Integer, List<String>> ProcessingBatchMap = new Map<Integer,List<String>>(); //Constructor public MyCtl() { MapKey = -1; IsPollerEnabled = false; IsTableVisible = false; } //Custom Type public class MyCustomType { public MyCustomType(String pField1, String pField2) { Field1 = pField1; Field2 = pField2; } public String Field1 {get; set;} public String Field2 {get; set;} } //ItemResultList public List<MyCustomType> getItemResultList () { return ItemResultList; } //DisplayItemResultList public List<MyCustomType> getDisplayItemResultList() { return DisplaySFDCFieldList; } public void setDisplayItemResultList(List<MyCustomType> pDisplayItemResultList) { DisplayItemResultList = new List<MyCustomType>(); DisplayItemResultList.addAll(pDisplayItemResultList); } //ItemList public List<SelectOption> getItemList() { this.getAllItemsToSelect(); return ItemList; } //SelectedNameList public List<String> getSelectedNameList() { return SelectedNameList; } public void setSelectedNameList(String[] pSelectedNameList) { //Clear any existing selected options if any if(!SelectedNameList.isEmpty()) SelectedNameList.clear(); //Identify the list of selected object names for(String strSelectedName : pSelectedNameList) { SelectedNameList.add(strSelectedName); } } //SelectedCategory public String getSelectedCategory () { return SelectedCategory; } public void setSelectedCategory (String pSelectedCategory) { SelectedCategory = pSelectedCategory);} //CategoryLblList public List<Selectoption> getCategoryLblList() { if(CategoryLblList.isEmpty()) { CategoryLblList.add(new Selectoption('all', 'All')); CategoryLblList.add(new Selectoption('new', 'New')); CategoryLblList.add(new Selectoption('old', 'Old')); } return CategoryLblList; } public Integer MapKey{get; set;} public Boolean IsPollerEnabled{get; set;} public Boolean IsTableVisible{get; set;} public Integer BatchMapSize {get; set;} //METHODS public void getAllItemsToSelect() { /* Query and get values to select*/ for(String strItemName : ItemNameList) ItemList.add(strItemName) } //Executes when the View On Screen is clicked and enables the action poller public void processSelection() { this.createProcessingBatches(); //Create batches of the selected names if(MapKey == -1) this.MapKey = 1; if(!SFDCFieldList.isEmpty()) //Clear the old values SFDCFieldList.clear(); this.IsPollerEnabled = true; //Activate the poller } //Divides the selected names into batch of 25 elements each and stores in a Map public void createProcessingBatches() { Integer intCount = 1; List<String> BatchList = new List<String>(); //Clear the old elements to store new elements ProcessingBatchMap.clear(); for(Integer intIndex = 0; intIndex < (SelectedNameList.size(); intIndex++) { BatchList.add((SelectedNameList.get(intIndex)); if((Math.mod(BatchList.size(), 25) == 0) || intIndex == (SelectedNameList.size() - 1) { ProcessingBatchMap.put(intCount, BatchList); ++intCount; //increment the count BatchList = new List<String>(); //to store elements for new batch } } BatchMapSize = ProcessingBatchMap.size(); } public void getAllFieldValues() { /* Fetch List<FieldValues>*/ for(objFld : List<FieldValues>) { ItemResultList.add(new MyCustomType(objFld.Field1, objFld.Field2));} this.updateMapIndex(); } private void updateMapIndex() { if((this.MapKey + 1) > ProcessingBatchMap.size()){ IsPollerEnabled = false; IsTableVisible = true; MapKey = 1; DisplayItemResultList = ItemResultList;
 } else ++MapKey; } }//MyCtl Ends

 

   This might help !!

bob_buzzardbob_buzzard

In the method that is backing the list:

 

//DisplayItemResultList
	 public List<MyCustomType> getDisplayItemResultList()
	 { return DisplaySFDCFieldList; }

 

where does DisplaySFDCFieldList get defined/set up? I can't find any references to it in your controller.

Jigar ShahJigar Shah

Sorry Bob.. my bad...

Its should have been...

 

 

//DisplayItemResultList
	 public List<MyCustomType> getDisplayItemResultList()
	 { return DisplayItemResultList; }

 

good catch !!.. thanks for the reply...

 

bob_buzzardbob_buzzard

So are you still having problems with the page?

Jigar ShahJigar Shah

Yes I am still having problems with the page. Is this problem occuring because the output panel containing the dataTable is being rerendered everytime ??

Jigar ShahJigar Shah

Hi Bob,

 

Thanks for replying. Finally I managed to figure out the issue.

 

The issue was that I would add the values from IntermediateList to the DisplayList, list that was bound with the datatable on UI using DisplayList.addAll(IntermediateList). Then I would clear the IntermediateList.

 

Solution:

What happens is that the DisplayList would still hold the reference of IntermediateList and thus would contain no elements when the IntermediateList was cleared i.e. in Apex, a Shallow Copy of the collection is created when we say List.addAll(ListName) . Hence instead of clearing the list everytime, I created a new reference for the intermediate List.

 

I think the Controller code below would explain it better.

 

public class ProcessData{

//List that is bound to the dataTable on UI
List<MyCustomType> DisplayList = new List<MyCustomType>(); //List that holds the ouput data after each batch is processed
List<MyCustomType> IntermediateList = new List<MyCustomType>(); public void processSelectedObjects() { /* Process selected values and populate the IntermediateList */ } private void updateMapIndex() { DisplayList.addAll(IntermediateList); IntermediateList.clear(); //Code causing the irregular behaviour IntermediateList = new List<MyCustomType> //Correct Code } }

 

Appreciate all the help, Bob. Cheers !!

This was selected as the best answer