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
Michael Friedman 7Michael Friedman 7 

Visualforce sort two apex:pageblocktable elements on same page

I am new to visualforce and am having trouble getting sorting working on my page. My page has two pageblocktables on it that pull data from two different object dynamically based on fields that users input into a custom setting. I would like both tables to be sortable independently. The solution I have is working for one table, but not the other. As far as I can tell the code for both tables is identical. Not sure why there is a difference in behavior.

The table that works is pulling data from an object called Timesheet__c. It is the second table on the page. The table that doesn't is the first and is pulling from an object called Expense_Line__c.

Any help would be much appreciated!

Here is my page:
 
<apex:page controller="invoicePickerClass" lightningStylesheets="TRUE">
  <apex:form>

    <!-- Output panel to make the buttons live side-by-side -->
    <apex:outputPanel id="thePanel" layout="block">

      <!-- Button to get Save changes and stay on form -->
      <apex:commandButton action="{!getSelected}" value="Save" id="Save"/>

      <!-- Button to Save changes and return to Invoice -->
      <apex:commandButton action="{!getSelectedandLeave}" value="Save and Done" id="SaveAndDone"/>
    </apex:outputPanel>
    
    <!--Panel grid that holds the two tables  -->
    <!-- <apex:panelGrid columns="1" width="100%" columnClasses="pgCol"> -->
    
      <!-- Expense Line Table -->
      <apex:pageBlock title="Expense Line List" id="expenseLines">
        <apex:pageBlockSection columns="1">
            
            <!-- ExpenseLine List -->
          <apex:pageBlockTable value="{! ExpenseLines }" var="el" rendered="{!(expenseLines.size != 0)}" cellpadding="5">
            
            <!-- Checkbox Column -->
            <apex:column >
              <apex:facet name="header">
                <apex:inputCheckbox styleClass="checkEL" value="{!el.isSelectedEL}" onclick="checkAll(this,'checkEL')"/>
              </apex:facet>
                <apex:inputCheckbox styleClass="checkEL" value="{!el.isSelectedEL}" id="checkedone">
                </apex:inputCheckbox>
            </apex:column>

            <!-- Dynamic Table Columns -->
            <apex:repeat value="{!ELCol}" var="elCol">
            <apex:column> 
              <apex:facet name="header">
                <apex:commandLink action="{!elSortDir}" immediate="true" reRender="expenseLines" value="{!$ObjectType.Expense_Line__c.Fields[elCol].Label }">
                  <apex:param name="sortParam" assignTo="{!elSort}" value="{!elCol}"/>
                  <apex:outputPanel>
                    <apex:image value="{!IF(elSort = elCol,$Resource[elSortIMG], $Resource.Sortable)}"/>
                  </apex:outputPanel>
                </apex:commandLink>
              </apex:facet>
              <apex:outputField value="{! el.exp[elCol] }"/> 
            </apex:column>
            </apex:repeat>

            <!-- Make table inline editable -->
            <apex:inlineEditSupport event="ondblClick"/>
          </apex:pageBlockTable>

          <!-- Text to display if no data in the table -->
          <apex:outputText rendered="{!(expenseLines.size = 0)}" value="There are no Expenses to display." />
        </apex:pageBlockSection>            
      </apex:pageBlock>


      <!-- Timesheets Table -->
      <apex:pageBlock title="TimeSheet List" id="timeSheets">
        <apex:pageBlockSection columns="1">
            
          <!-- TimeSheet List -->
          <apex:pageBlockTable value="{! TimeSheets }" var="ts" rendered="{!(TimeSheets.size != 0)}" cellpadding="5">
                
            <!-- Checkbox Column -->
            <apex:column>
              <apex:facet name="header">
                <apex:inputCheckbox styleClass="checkTS" value="{!ts.isSelectedTS}" onclick="checkAll(this,'checkTS')"/>
              </apex:facet>
                <apex:inputCheckbox styleClass="checkTS" value="{!ts.isSelectedTS}" id="checkedone">
                </apex:inputCheckbox>
            </apex:column>

            <!-- Dynamic table column -->
            <apex:repeat value="{!TSCol}" var="tsCol">
            <apex:column>
              <apex:facet name="header">
                <apex:commandLink action="{!tsSortDir}" immediate="true" reRender="timeSheets" value="{!$ObjectType.Timesheet__c.Fields[tsCol].Label }">
                  <apex:param name="sortParam" assignTo="{!tsSort}" value="{!tsCol}"/>
                  <apex:outputPanel>
                    <apex:image value="{!IF(tsSort = tsCol,$Resource[tsSortIMG], $Resource.Sortable)}"/>
                  </apex:outputPanel>
                </apex:commandLink>
              </apex:facet>
              <apex:outputField value="{! ts.tsh[tsCol] }"/> 
            </apex:column>
            </apex:repeat>

            <!-- Make table inline editable -->
            <apex:inlineEditSupport event="ondblClick"/>
          </apex:pageBlockTable>

          <!-- Text to display if no data for table -->
          <apex:outputText rendered="{!(TimeSheets.size = 0)}" value="There are no TimeSheets to display."/>
        </apex:pageBlockSection>
      </apex:pageBlock>
   
      
    <!-- </apex:panelGrid> -->
  </apex:form>

  <!-- Script that runs when check all is selected -->
  <script>
    function checkAll(cb, check)
    {
        var inputElem = document.getElementsByClassName(check);
        for(var i=0; i<inputElem.length; i++)
        {
            if(inputElem[i].id.indexOf("checkedone")!=-1)
            inputElem[i].checked = cb.checked;
        }
    }
  </script>
  
  <!-- Style needed if the tables are moved side-by-side -->
  <style type="text/css">
    .pgCol{
      width:50%;
    }
  </style>
</apex:page>

Here is my apex code:
 
public with sharing class invoicePickerClass {

	//URL and other Parameters 
	public String invId {get; set;}
	public String projId {get; set;}
	public static String recId {get; set;}

	//Sort Variables
	public String tsSort {get; set;}
	public String tsSortLast {get; set;}
	transient String tsSortDir {get; set;}
	public String tsSortDirLast {get; set;}
	
	public String elSort {get; set;}
	public String elSortLast {get; set;}
	transient String elSortDir {get; set;}
	public String elSortDirLast {get; set;}

	//Sort images
    public String tsSortIMG {get;set;}
    public String elSortIMG {get;set;}
	
	//Lists for synamic columns
	List<String> tsCol = new List<string>();
	List<String> elCol = new List<string>();

    //Wrapper class data
    public List<tsWrapper> tsList {get; set;}
    List<Timesheet__c> selectedTimeSheets = new List<Timesheet__c>();
    List<Timesheet__c> unSelectedTimeSheets = new List<Timesheet__c>();
    public List<elWrapper> elList {get; set;}
    List<Expense_Line__c> selectedExpenseLines = new List<Expense_Line__c>();
    List<Expense_Line__c> unSelectedExpenseLines = new List<Expense_Line__c>();

    //method to populate time sheet table using wrapper class
    public  List<tsWrapper> getTimeSheets() {

    		invId = Apexpages.currentPage().getparameters().get('invId');

    		projId = Apexpages.currentPage().getparameters().get('projId');

    		system.debug(tsSort+tsSortLast+tsSortDir+tsSortDirLast+tsSortIMG);

    		if(tsSort == NULL){tsSort = 'Date__c'; tsSortDir = 'DESC';}

    		system.debug(tsSort+tsSortLast+tsSortDir+tsSortDirLast+tsSortIMG);

    		tsSortDir();
    		tsSortLast = tsSort;

    		system.debug(tsSort+tsSortLast+tsSortDir+tsSortDirLast+tsSortIMG);

    		List<string> columns = getColumns('Timesheet__c');

    		String qry = 'select Id, Invoice__c, Project__c, '+String.join(columns, ',')+' FROM Timesheet__c WHERE Project__c = :projId AND (Invoice__c = \'\' OR Invoice__c = :invId) ORDER BY '+tsSort+' '+tsSortDir;
            
           // if(tsList == NULL){
            	tsList = new List<tsWrapper>();
            	for(Timesheet__c tsh : Database.query(qry)){
            		tsList.add(new tsWrapper(tsh));
            			
            	}
           // }

            return tsList;
    }

    
    //method to populate expense line table using wrapper class
    public  List<elWrapper> getExpenseLines() {

    		invId = Apexpages.currentPage().getparameters().get('invId');

    		projId = Apexpages.currentPage().getparameters().get('projId');

    		system.debug(elSort+elSortLast+elSortDir+elSortDirLast+elSortIMG);

    		if(elSort == NULL){elSort = 'Date__c'; elSortDir = 'DESC';}

    		system.debug(elSort+elSortLast+elSortDir+elSortDirLast+elSortIMG);

    		elSortDir();
    		elSortLast = elSort;

    		system.debug(elSort+elSortLast+elSortDir+elSortDirLast+elSortIMG);

    		List<string> columns = getColumns('Expense_Line__c');

    		String qry = 'select Id, Invoice__c, Project__c, '+String.join(columns, ',')+' FROM Expense_Line__c WHERE Project__c = :projId AND (Invoice__c = \'\' OR Invoice__c = :invId) ORDER BY '+elSort+' DESC';

            //if(elList == NULL){
            elList = new List<elWrapper>();
            	for(Expense_Line__c exp : Database.query(qry)){
            		elList.add(new elWrapper(exp));	
            	}
            //}

            return elList;
    }

    public List<String> getColumns(string obj){
    	List<InvoicePicker__c> ipCol = new List<InvoicePicker__c>();
    	List<String> columns = new List<String>();
    	if(obj=='Timesheet__c'){
    		ipCol = [select Field__c FROM InvoicePicker__c WHERE Object__c = :obj ORDER BY Column_Order__c ASC];
    	}
    	else if(obj=='Expense_Line__c'){
    		ipCol = [select Field__c FROM InvoicePicker__c WHERE Object__c = :obj ORDER BY Column_Order__c ASC];
    	}
    	for(InvoicePicker__c ip : ipCol){
    		columns.add(ip.Field__c);
    	}
    	return columns;
    }

    //method for dynamic columns for Timesheets
    public List<String> getTSCol(){
    	tsCol.clear();
    	tsCol.addAll(getColumns('Timesheet__c'));
    	return tsCol;

    }

    //method for dynamic columns for Expense Lines
    public List<String> getELCol(){
    	elCol.clear();
    	elCol.addAll(getColumns('Expense_Line__c'));
    	return elCol;

    }


    //Sort data table action
    public void tsSortDir() {
		
		if(tsSortLast == tsSort && tsSortDirLast == 'DESC'){tsSortDir = 'ASC';}
		else if(tsSortLast == tsSort && tsSortDirLast == 'ASC'){tsSortDir = 'DESC';}
		else{tsSortDir = 'DESC';}

		tsSortDirLast = tsSortDir;
		tsSortIMG = tsSortDir;
    }

    public void elSortDir() {
		
		if(elSortLast == elSort && elSortDirLast == 'DESC'){elSortDir = 'ASC';}
		else if(elSortLast == elSort && elSortDirLast == 'ASC'){elSortDir = 'DESC';}
		else{elSortDir = 'DESC';}

		elSortDirLast = elSortDir;
		elSortIMG = elSortDir;
    }

	//timesheet wrapper class
    public class tsWrapper{
        public Timesheet__c tsh {get; set;}
        public Boolean isSelectedTS {get; set;}
        public tsWrapper(Timesheet__c ts){
            tsh = ts;
            if(tsh.Invoice__c != NULL){
            	isSelectedTS = true;
            }
            else{isSelectedTS = false;}
        }
    }

    //Expense Line Wrapper
    public class elWrapper{
        public Expense_Line__c exp{get; set;}
        public Boolean isSelectedEL {get; set;}
        public elWrapper(Expense_Line__c el){
            exp = el;
            if(exp.Invoice__c != NULL){
            	isSelectedEL = true;
            }
            else{isSelectedEL = false;}
        }
    }


    //Original Processing of selected items
    public PageReference getSelectedEL(){
        selectedExpenseLines.clear();
        unSelectedExpenseLines.clear();
        for(elWrapper elWrapper : this.elList){
	        if(elWrapper.isSelectedEL == true) {
		        selectedExpenseLines.add(elWrapper.exp);
	    	}
	    	if(elWrapper.isSelectedEL == false){
	    		unSelectedExpenseLines.add(elWrapper.exp);
	    	}
	    }
       	return null;
    }

	public PageReference getSelectedTS(){
        selectedTimeSheets.clear();
        unSelectedTimeSheets.clear();

        for(tsWrapper tsWrapper : this.tsList){
	        if(tsWrapper.isSelectedTS == true) {
		        selectedTimeSheets.add(tsWrapper.tsh);
	    	}
	    	if(tsWrapper.isSelectedTS == false){
	    		unSelectedTimeSheets.add(tsWrapper.tsh);
	    	}
	    }
       	return null;
    }

     //wrapper class for Comparable Interface
    
    //public class sortN implements Comparable {
    //    public String sortValue {get;set;}    
        
    //    public sortN(String sortName) {
    //        sortValue = sortName;
    //    }
        
    //    public Integer compareTo(Object ObjToCompare) {
    //        return sortValue.CompareTo(((sortN)ObjToCompare).sortValue);
    //    }
    //}
     
    


    //Button method

    public PageReference getSelected(){
    	//get all selected and unselected records
		getSelectedEL();
    	getSelectedTS();
    	
    	//process and update all selected and unselected records
    	processTimeSheets();
    	processExpenseLines();
    	

    	
    	return NULL;
    }

     public PageReference getSelectedandLeave(){
    	//get all selected and unselected records
    	getSelectedEL();
    	getSelectedTS();
    	
    	//process and update all selected and unselected records
    	processTimeSheets();
    	processExpenseLines();
    	
    	//navigate back to invoice record
    	PageReference pr = new PageReference('/'+invId);
        system.debug(pr);
        pr.setRedirect(true);
    	
    	return pr;
    }

    public void processTimeSheets(){

    	List<Timesheet__c> processedTimeSheets = new List<Timesheet__c>();

    	for(Timesheet__c t : selectedTimeSheets){
    		Timesheet__c newTimeSheet = new Timesheet__c(id=t.Id);
    		newTimeSheet.Invoice__c = invId;
    		Map<String, SObjectField> m = Timesheet__c.SObjectType.getDescribe().fields.getMap();
    		for(String f : tsCol){
    			DescribeFieldResult r = m.get(f).getDescribe();
		      	if(r.isUpdateable()){
    				newTimeSheet.put(f, t.get(f));
		      	}	
    		}
    		processedTimeSheets.add(newTimeSheet);
    	}

    	for(Timesheet__c t : unSelectedTimeSheets){
    		Timesheet__c newTimeSheet = new Timesheet__c(id=t.Id);
    		newTimeSheet.Invoice__c = null;
    		Map<String, SObjectField> m = Timesheet__c.SObjectType.getDescribe().fields.getMap();
    		for(String f : tsCol){
    			DescribeFieldResult r = m.get(f).getDescribe();
		      	if(r.isUpdateable()){
    				newTimeSheet.put(f, t.get(f));
		      	}	
    		}
    		processedTimeSheets.add(newTimeSheet);
    	}

    	update processedTimeSheets;
    }

    public void processExpenseLines(){

    	List<Expense_Line__c> processedExpenseLines = new List<Expense_Line__c>();

    	for(Expense_Line__c e : selectedExpenseLines){
    		Expense_Line__c newExpenseLines = new Expense_Line__c(id=e.Id);
    		newExpenseLines.Invoice__c = invId;
    		Map<String, SObjectField> m = Expense_Line__c.SObjectType.getDescribe().fields.getMap();
    		for(String f : elCol){
    			DescribeFieldResult r = m.get(f).getDescribe();
		      	if(r.isUpdateable()){
    				newExpenseLines.put(f, e.get(f));
		      	}
    			
    		}

    		processedExpenseLines.add(newExpenseLines);
    	}

    	for(Expense_Line__c e : unSelectedExpenseLines){
    		Expense_Line__c newExpenseLines = new Expense_Line__c(id=e.Id);
    		newExpenseLines.Invoice__c = null;
    		Map<String, SObjectField> m = Expense_Line__c.SObjectType.getDescribe().fields.getMap();
    		for(String f : elCol){
    			DescribeFieldResult r = m.get(f).getDescribe();
		      	if(r.isUpdateable()){
    				newExpenseLines.put(f, e.get(f));
    			}
    		}

    		processedExpenseLines.add(newExpenseLines);
    	}
    	update processedExpenseLines;
    }

}

 
Best Answer chosen by Michael Friedman 7
Michael Friedman 7Michael Friedman 7
Nayana, thanks for the reply. Your prompt got me looking at a couple of things with fresh eyes and it turns out I just hadn't put the sort direction variable into the query string for the second table. Once I did that, it worked perfectly.

Thanks again for the help.

All Answers

Nayana KNayana K
Yes, code looks same for both the sortings.

I am not sure whether these really make a difference. Still, please give a try:
1. Change names of apex:param like sortParam1 for first table and sortParam2 for second table.
2. If that doesn't work, wrap <apex:pageBlock title="Expense Line List" id="expenseLines"> pagblock inside an outputpanel and rerender the outputpanel.
Michael Friedman 7Michael Friedman 7
Nayana, thanks for the reply. Your prompt got me looking at a couple of things with fresh eyes and it turns out I just hadn't put the sort direction variable into the query string for the second table. Once I did that, it worked perfectly.

Thanks again for the help.
This was selected as the best answer