Friday, 30 July 2010

"Check All" checkbox in an apex:dataTable (without using scripts)

Recently, a business requirement emerged for a client whereby a user needed to be able to select a number of contacts from a data table, and then carry out some business logic on those selected clients. The process of selecting several contacts using a wrapper class is easy enough, but what about a simple way to select and deselect all of the records in one go? This can be achieved placing a checkbox in the header element of the column. I found an example solution that uses a javascript function embedded on the page to copy the true/false value of the header checkbox to all the boxes in the column.

This certainly does the job, but I found from a design perspective it had some limitations. The javascript function it calls gets the elements by tag, specifically the input tag, which may be used elsewhere if the page you are designing contains more than just this data table. Also, placing selection controls in scripts divides the selection logic between two entities; the page and the apex controller class.

With this in mind, I started to think about how I could develop an equivalent solution that uses logic inside the controller to change the state of all of the checkboxes. This is the solution I came up with:

Controller:
public class CheckboxCheckAllController {
 
 public List<ContactWrapper> allContacts { get; set; }
 public Boolean allChecked { get; set; }
 
 public CheckboxCheckAllController () {
  allContacts = new List<ContactWrapper>();
  allChecked = false;
  
  for(Contact contact: [select Name, Title, Department, Email from Contact ]){ 
   allContacts.add(new ContactWrapper(contact));
  } 
 }
 
 public PageReference CheckAll(){
  
  for(ContactWrapper contact : allContacts){
   contact.selected = allChecked;
  }
  
  return null;
 }
 
 public PageReference ProcessSelectedContacts(){
  
  for (ContactWrapper contactWrapper : allContacts ){
   if(contactWrapper.selected == true){
    /* Do Some Processing */
   }
  }
  
  return null;
 }

 public class ContactWrapper {
  
  public Contact con{get; set;}
        public Boolean selected {get; set;}
        
        public ContactWrapper(Contact c){
            con = c;
            selected = false;
        }
        
 }
}

Page:
<apex:page controller="CheckboxCheckAllController">

  <apex:form>

    <apex:dataTable value="{!allContacts}" var="c" id="contactsTable">
      <apex:column>
        <apex:facet name="header">
          <apex:inputCheckbox value="{!allChecked}">
            <apex:actionSupport event="onclick" action="{!CheckAll}"
                                rerender="contactsTable"/>
          </apex:inputCheckbox>
        </apex:facet>
        <apex:inputCheckbox value="{!c.selected}"/>
      </apex:column>
      <apex:column value="{!c.con.Name}" headervalue="Full Name"/>
      <apex:column value="{!c.con.Title}" headervalue="Title"/>
      <apex:column value="{!c.con.Department}" 
                   headervalue="Department"/>
      <apex:column value="{!c.con.Email}" headervalue="Email"/>
    </apex:dataTable>

    <apex:commandButton action="{!ProcessSelectedContacts}" 
                        value="Process Selected Contacts"/>

  </apex:form>
</apex:page>

Page In Browser:


Admittedly, it provides a slightly slower response time than the pure javascript, but I think this provides benefits in singularity of location of control code and adaptability, as you can control the behaviour of the selection using array logic.

2 comments: