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
Thomas FullerThomas Fuller 

Apex/Visualforce - Updating an Apex List Using a Search-Layout-Like Checkbox System

Hey Everyone,

Right now, I’m developing a new feature for our org, and I was wondering if any developers would mind looking over my code and guiding me to the best possible solution.

For this project, I am trying to create a visualforce page where the user can search through our org’s list of contacts and leads. The functionality is a lot like what you would see on a search layout. The user would enter filter keywords, a queried list would display in front of them, and then the user could select multiple records to pass onto an action or button.

User-added image

Luckily, I was able to successfully handle selecting multiple records and storing them in an Apex list. However, I am having an issue with developing a way for the user to see how many current records are selected, and see this number in real-time.

I tried to solve this by creating an integer in my Apex controller that would be increased if the user checks a checkbox, and decrease if the user unchecks the box. 
 
 
This seems that it works fine for selecting individual records, but I’m not sure how to make sure the “select all checkbox” updates the integer correctly, especially since I’m working with two different objects.

 
User-added image
 
User-added image

Here is my Visualforce Page:
<!-- Below is the Visualforce Markup for the two main pageBlockTables that each display the
      contact and lead records that were queried from the Apex Controller -->
	  
<apex:pageBlockTable id="results" value="{!contacts}" var="item">
		<apex:column width="23">
			<apex:facet name="header">
				<input type="checkbox" id="contactCB" onchange="checkAll(this);"/>
			</apex:facet>
			<apex:inputCheckbox styleClass="columnCheck" value="{!item.checked}" onchange="updateSelected(this)"/>
		</apex:column>
			<apex:column value="{!item.conObj.firstname}"/>
			<apex:column value="{!item.conObj.lastname}"/>
			<apex:column value="{!item.conObj.Title}"/>
			<apex:column value="{!item.conObj.account.Industry}"/>
			<apex:column value="{!item.conObj.Phone}"/>
			<apex:column value="{!item.conObj.Email}"/>
			<apex:column value="{!item.conObj.MailingCity}"/>
			<apex:column value="{!item.conObj.MailingState}"/>
		</apex:pageBlockTable>
		<apex:pageBlockTable id="resultsLead" value="{!leadList}" var="item">
		<apex:column width="23">
			<apex:facet name="header">
				<input type="checkbox" id="leadCB" onchange="checkAll2(this);"/>
			</apex:facet>
			<apex:inputCheckbox styleClass="columnCheck2" value="{!item.checked}" onchange="updateSelected(this)"/>
		</apex:column>
			<apex:column value="{!item.leadObj.firstname}"/>
			<apex:column value="{!item.leadObj.lastname}"/>
			<apex:column value="{!item.leadObj.Title}"/>
			<apex:column value="{!item.leadObj.Industry}"/>
			<apex:column value="{!item.leadObj.Company}"/>
			<apex:column value="{!item.leadObj.NumberOfEmployees}"/>
			<apex:column value="{!item.leadObj.AnnualRevenue}"/>
			<apex:column value="{!item.leadObj.Phone}"/>
			<apex:column value="{!item.leadObj.Email}"/>
			<apex:column value="{!item.leadObj.City}"/>
			<apex:column value="{!item.leadObj.State}"/>
</apex:pageBlockTable>

Here is my Javascript:
function checkAll(e) {
	if (j$(e).is(':checked')) {
		j$('.columnCheck').prop('checked', true);
	} else {
		j$('.columnCheck').prop('checked', false);
	}
}
function checkAll2(e) {
	if (j$(e).is(':checked')) {
		j$('.columnCheck2').prop('checked', true);
	} else {
		j$('.columnCheck2').prop('checked', false);
	}
}
function updateSelected(e) {
	if (j$(e).is(':checked')) {
		upSel();
	} else {
		downSel();
	}
}

Here is another slice of my visualforce:
<apex:actionFunction name="mainSearch" action="{!runMarketingSearch}" rerender="results, resultsLead, debug, selectedQuery, totalQuery">
   <apex:param name="title_mL" value="" />
   <apex:param name="industry_mL" value="" />
   <apex:param name="company_mL" value="" />
   <apex:param name="revenue_mL" value="" />
   <apex:param name="employNum_mL" value="" />
   <apex:param name="cityInput" value="" />
   <apex:param name="stateInput" value="" />
  </apex:actionFunction>
  <apex:actionFunction name="upSel" action="{!increaseSelected}" rerender="selectedQuery, totalQuery"/>
  <apex:actionFunction name="downSel" action="{!decreaseSelected}" rerender="selectedQuery, totalQuery"/>
<table width="100%" border="0">
<tr> 
  <td style="width:375px" valign="top">
 
   <apex:pageBlockButtons >
    <apex:commandButton action="{!saveObjects}" value="Save"/>
    <apex:commandButton action="{!next}" value="See Results"/>
   </apex:pageBlockButtons>
...

Here is my Apex Controller:
public with sharing class mLSearchController{
	private String soqlCon {get;set;}
	private String soqlLead {get;set;}
	public List<contactWrapper> contacts {get;set;}
	public List<contactWrapper> leadList {get;set;}
	public List<contactWrapper> selectedList {        
		get {
            if (selectedList == null) selectedList = new List<contactWrapper>();
            return selectedList;
        }
        set;
        }
	public List<contactWrapper> debugSoql {
	    get { return selectedList; }
	    set;
  	}
  	public Integer queryTotal {get;set;}
  	public Integer queryContactSel {get;set;}
  	public Integer queryLeadTotalSel {get;set;}
  	public Integer querySel {get;set;}
	
	
	public mLSearchController() {}
  
	public void runQuery() {

		try {

		} catch (Exception e) {
			ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Ooops!'));
		}
	}
	
	public PageReference runMarketingSearch() {
		String title_mL = Apexpages.currentPage().getParameters().get('title_mL');
		String industry_mL = Apexpages.currentPage().getParameters().get('industry_mL');
		String company_mL = Apexpages.currentPage().getParameters().get('company_mL');
		String revenue_mL = Apexpages.currentPage().getParameters().get('revenue_mL');
		String employNum_mL = Apexpages.currentPage().getParameters().get('employNum_mL');
		String cityInput = Apexpages.currentPage().getParameters().get('cityInput');
		String stateInput = Apexpages.currentPage().getParameters().get('stateInput');
		soqlCon = 'select firstname, lastname, Title, Email, Phone, account.Industry, MailingCity, MailingState, LastActivityDate from Contact WHERE account.name != null and LastActivityDate != LAST_N_DAYS:60';
		soqlLead = 'select firstname, lastname, Title, Phone, Email, Industry, Company, NumberOfEmployees, AnnualRevenue, City, State from Lead WHERE Company != null and LastActivityDate != LAST_N_DAYS:60';
      	if (!title_mL.equals('')) {
      		soqlCon += ' and Title LIKE \'%' + String.escapeSingleQuotes(title_mL) + '%\'';
      		soqlLead += ' and Title LIKE \'%' + String.escapeSingleQuotes(title_mL) + '%\'';
      	}
      	if (!industry_mL.equals('')) {
      		soqlCon += ' and account.Industry LIKE \'%' + String.escapeSingleQuotes(industry_mL) + '%\'';
      		soqlLead += ' and Industry LIKE \'%' + String.escapeSingleQuotes(industry_mL) + '%\'';
      	}
      	if (!cityInput.equals('')) {
      		soqlCon += ' and MailingCity LIKE \'' + String.escapeSingleQuotes(cityInput) + '%\'';
      		soqlLead += ' and City LIKE \'' + String.escapeSingleQuotes(cityInput) + '%\'';
      	}
      	if (!stateInput.equals('')) {
      		soqlCon += ' and MailingState LIKE \'' + String.escapeSingleQuotes(stateInput) + '%\'';
      		soqlLead += ' and State LIKE \'' + String.escapeSingleQuotes(stateInput) + '%\'';
      	}
      	if (!company_mL.equals('')) {
      		soqlLead += ' and Company LIKE \'%' + String.escapeSingleQuotes(company_mL) + '%\'';
      	}
      	
      	//if (!revenue_mL.equals('')) soqlLead += ' and AnnualRevenue LIKE \'%' + String.escapeSingleQuotes(revenue_mL) + '%\'';
      	if (revenue_mL == 'tier1_mL') {
      		soqlLead += ' and (AnnualRevenue < 1000000.00)';
      	} else if (revenue_mL == 'tier2_mL') {
      		soqlLead += ' and (AnnualRevenue >= 1000000.00 AND AnnualRevenue < 5000000.00)';
      	} else if (revenue_mL == 'tier3_mL') {
      		soqlLead += ' and (AnnualRevenue >= 5000000.00 AND AnnualRevenue < 10000000.00)';
      	} else if (revenue_mL == 'tier4_mL') {
      		soqlLead += ' and (AnnualRevenue >= 10000000.00 AND AnnualRevenue < 25000000.00)';
      	} else if (revenue_mL == 'tier5_mL') {
      		soqlLead += ' and (AnnualRevenue >= 25000000.00 AND AnnualRevenue < 50000000.00)';
      	} else if (revenue_mL == 'tier6_mL') {
      		soqlLead += ' and (AnnualRevenue >= 50000000.00 AND AnnualRevenue < 100000000.00)';
      	} else if (revenue_mL == 'tier7_mL') {
      		soqlLead += ' and (AnnualRevenue >= 100000000.00 AND AnnualRevenue < 250000000.00)';
      	} else if (revenue_mL == 'tier8_mL') {
      		soqlLead += ' and (AnnualRevenue >= 250000000.00 AND AnnualRevenue < 500000000.00)';
      	} else if (revenue_mL == 'tier9_mL') {
      		soqlLead += ' and (AnnualRevenue >= 500000000.00 AND AnnualRevenue < 2500000000.00)';
      	} else if (revenue_mL == 'tier10_mL') {
      		soqlLead += ' and (AnnualRevenue >= 2500000000.00)';
      	} else {
      		
      	}
      	
      	
      	if (employNum_mL == 'tier1_mL') {
      		soqlLead += ' and (NumberOfEmployees >= 1 AND NumberOfEmployees <= 100)';
      	} else if (employNum_mL == 'tier2_mL') {
      		soqlLead += ' and (NumberOfEmployees >= 101 AND NumberOfEmployees <= 500)';
      	} else if (employNum_mL == 'tier3_mL') {
      		soqlLead += ' and (NumberOfEmployees >= 501 AND NumberOfEmployees <= 3500)';
      	} else if (employNum_mL == 'tier4_mL') {
      		soqlLead += ' and (NumberOfEmployees > 3500)';
      	} else {
      		
      	}
      		contacts = new List<contactWrapper>();
      		leadList = new List<contactWrapper>();
			for (Contact conRec : Database.query(soqlCon + ' limit 20')) {
				contactWrapper cr = new contactWrapper(conRec);
				contacts.add(cr);
			}
			for (Lead leadRec : Database.query(soqlLead + ' limit 20')) {
				contactWrapper lr = new contactWrapper(leadRec);
				leadList.add(lr);
			}
			queryTotal = contacts.size() + leadList.size();
			querySel = selectedList.size();
    	return null;
	}
	
	public PageReference increaseSelected() {
		querySel++;
		return null;
	}
	
	public PageReference decreaseSelected() {
		querySel--;
		return null;
	}
	
	/*public PageReference contactAllSelected() {
		querySel = contacts.size();
		return null;
	}
	
	public PageReference leadAllSelected() {
		querySel = 0;
		return null;
	}*/
    
    public PageReference next() {
	selectedList.clear();
	for (contactWrapper cw : contacts) {
		if (cw.checked)
			selectedList.add(new contactWrapper(cw.conObj));
	}
	if (selectedList.size() > 0) {
		debugSoql = new List<contactWrapper>(selectedList);
		querySel = selectedList.size();
		return Page.ML_Search_Layout;
	} else {
		ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'Please select at least one Category.'));
		return null;
	}       
} 

}

Thanks so much for your time. I’m grateful for any help or suggestions on this issue.
Best Answer chosen by Thomas Fuller
KevinPKevinP
Thomas,

First off. Thanks for a great question. You included all the code, you phrased your question nicely, and you didn't ask us to do your homework. 
Secondly, Nice tight code. I like it. 

How about this:
// In the controller:

public PageReference doNothing() {
    return null;
}

// in the vf page:

<apex:actionFunction action="{!doNothing}" name="SelectAllCount" rerender="selectedQuery, totalQuery">
    <apex:param name="firstParam" assignTo="{!querySel}" value="" />
</apex:actionFunction>

then all you need todo, is use j$ to get a count of the objects you've selected and pass that as your parameter. soemthing like this:

var totalChecked = j$.grep(j$('.columnCheck2'), function(){
    if(j$(this).prop('checked')){
        return true;
    } else { return false;}
}.length;

so then it'd be 

SelectAllCount(totalChecked);


All Answers

KevinPKevinP
Thomas,

First off. Thanks for a great question. You included all the code, you phrased your question nicely, and you didn't ask us to do your homework. 
Secondly, Nice tight code. I like it. 

How about this:
// In the controller:

public PageReference doNothing() {
    return null;
}

// in the vf page:

<apex:actionFunction action="{!doNothing}" name="SelectAllCount" rerender="selectedQuery, totalQuery">
    <apex:param name="firstParam" assignTo="{!querySel}" value="" />
</apex:actionFunction>

then all you need todo, is use j$ to get a count of the objects you've selected and pass that as your parameter. soemthing like this:

var totalChecked = j$.grep(j$('.columnCheck2'), function(){
    if(j$(this).prop('checked')){
        return true;
    } else { return false;}
}.length;

so then it'd be 

SelectAllCount(totalChecked);


This was selected as the best answer
Thomas FullerThomas Fuller
Kevin,

Thanks so much for the kind words! For a newbie like me, it really means a lot for someone to call my code "tight". Your solution really did the trick. Before, I was trying to use my Apex controller to pass in the number of selected records to the front-end. It's actually an innovative idea of your's to just use the front-end GUI to pass info to the Apex controller. I suppose you could also take it further and just use Javascript/AJAX to keep a running, real-time count of the selected records. Regardless, thanks again for your solution and your feedback to an inexperienced developer!

-Thomas