31 March 2011

Adding a checkbox on a PageBlockTable

One of the common use cases I experienced developing customization in Salesforce is the ability for end user to selected multiple records from a list and process them. In Salesforce you can do this via client-side scripting using JavaScript. However, it is slow and you don't really have full control of the selected records specially when your doing multi-paging list.

The best approach I believed on doing this is to create an Apex wrapper class. The wrapper class is a class that will represent data for your table with additional properties. An example of using wrapper class is when you want to display an accounts and then select them using a checkboxes. Let's see how we approach that below.

We first create a wrapper class similar to this:

public class AccountWrapperCls {
     public Boolean isSelected {get;set;}
     public Account cAccount {get;set;}

     public AccountWrapperCls(Account cAccount){
          this.cAccount = cAccount;
     }
}

As you can see above, the class contains an account and a boolean property. The isSelected property will be referenced by our Visualforce page and will be rendered as a checkbox. To use this wrapper class we need to create a Visualforce controller for our page that will use the class above.

public class MyAccountListCntrlr {
     // PROPERTIES
     public List<AccountWrapperCls> acctList {get;set;}
     public Set<String> selAccountNames {get;set;}
     public Boolean hasSelAcct {get;set;}

     // CONSTRUCTOR
     public MyAccountListCntrlr(){
          acctList = new List<AccountWrapperCls>();
          selAccountNames = new Set<String>();

          for(Account a : [SELECT AccountNumber, Name 
          FROM Account WHERE AccountNumber != NULL 
          LIMIT 5]){
               acctList.add(new AccountWrapperCls(a));
          }
     }

     // METHODS
     public void displaySelectedAccountNumbers(){
          selAccountNames.clear();
          hasSelAcct = false;
          for(AccountWrapperCls cWrapper : acctList){
               if(cWrapper.isSelected){
                    hasSelAcct = true;
                    selAccountNames.add(cWrapper.cAccount.
                             AccountNumber);
               }
          }
     }
}

As you can see above, we created a list of type AccountWrapperCls which represents the account record and the additional isSelected property. Please note that we add a method called displaySelectedAccountNumbers() which gets all the account records whose isSelected value is set to TRUE. It also adds those selected account's account number to a set variable we defined in our class which we will use to display account numbers on the page.

Below is our Visualforce page:

<apex:page controller="MyAccountListCntrlr" tabStyle="Account">
   <apex:form >
      <apex:pageBlock title="Account List">
         <apex:pageBlockButtons >
            <apex:commandButton 
               value="Show Selected Accounts" 
               action="{!displaySelectedAccountNumbers}"/>
         </apex:pageBlockButtons>

         <!-- ACCOUNT LIST -->
         <apex:pageBlockTable 
           value="{!acctList}" 
           var="acctWrapper">
            <apex:column >
               <apex:inputCheckbox 
                 value="{!acctWrapper.isSelected}"/>
            </apex:column> 
            <apex:column 
              value="{!acctWrapper.cAccount.AccountNumber}"/>
            <apex:column 
              value="{!acctWrapper.cAccount.Name}"/>
         </apex:pageBlockTable>

         <!-- SELECTED ACCOUNT INFO -->
         <apex:pageBlockSection >
            <apex:outputPanel 
              layout="block" 
              rendered="{!hasSelAcct}">
               <apex:outputText 
                 value="Below are the selected account:"/>
               <br/>
               <apex:outputText 
                 value="{!selAccountNames}"/>
             </apex:outputPanel>
             <apex:outputPanel layout="block" 
               rendered="{!NOT(hasSelAcct)}">
                <br/>
                <apex:outputText value="No account selected."/>
             </apex:outputPanel>
         </apex:pageBlockSection>
      </apex:pageBlock>
   </apex:form>
</apex:page>

Note that we referenced our list variable while using the properties of our wrapper class in the controller .

14 comments:

  1. Just wanted to let you know this post was a big help when I was trying to set this up. Very clear example!

    ReplyDelete
  2. thanks a lot .. very clear example and work smoothly it help me a lot in my
    task

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. I deleted my comment because it wasn't right. Here's the updated one...

    The code definitely helped me see a different method of execution however I see 1 way that it can be improved for performance. The code isn't bulkafied and to increase run times or give you more flexibility when hitting limits, you'll want to do that.

    Instead of using the SOQL statement in the for loop do this...

    List ACCT=[SELECT AccountNumber, Name
    FROM Account WHERE AccountNumber!=NULL LIMIT 5]
    for(Account a :ACCT ]){
    acctList.add(new AccountWrapperCls(a));
    }

    ReplyDelete
    Replies
    1. Hi Ben. Thanks for the comment but I don't think removing the SOQL from the for loop improves performance. As a matter of fact if you remove the query from for loop it will give you 1 additional script statement executed and that is counted on against the maximum script executed. :-)

      Delete
  5. For some reason the <<>> is getting hidden but just add the object you will be using for your list as usual.

    ReplyDelete
  6. Thanks Alott Ben....I am a Beginner & learning logics,, YOU made this easy,, Cheers!!!!

    ReplyDelete
  7. I like the Controllers logic well... Easy to fetch...

    ReplyDelete
  8. Can anybody let me know how I can pass selected Ids to account merge wizard step-2?

    '/merge/accmergewizard.jsp?goNext&cid'+id+'cid='+id2'

    When I tried system displayed '/merge/accmergewizard.jsp?' page(Step-1) instead of step-2?

    Any help on this will be very helpful.

    ReplyDelete
  9. thank you.this is very good.plz let me know more if there is more tasks like this


    ReplyDelete
  10. Great overview of this, but how do you use the check boxes to pass IDs of the selected records back into a class?

    I've got an Add All (insert) here and I need the Ids of the selected records in my method. When I reference isSelected, even when records are selected, the value is always null. I'm trying to check for a true to create a string, List, whatever, with those selected record Ids.

    ReplyDelete
  11. I'm getting an "list indices must be integers, not str" error when I try to compile the MyAccountListCntrlr. The AccountWrapperCls compiles fine but the other complains. Any ideas how to fix this?

    ReplyDelete
  12. I would like to post code on this website, but existing limit of 4096 characters error is being thrown. Could you please increase the limit ?

    ReplyDelete