• Scott Broam
  • NEWBIE
  • 0 Points
  • Member since 2019

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 1
    Likes Given
  • 0
    Questions
  • 2
    Replies

Sometimes we need to build a table for showing grouped records by a particular field. Here I show one way to accomplish this. 

For example, suppose we want to show all contacts grouped by company as shown in the following image.

Table of Contacts grouped by Account

In order to group cells in a html table we could use the attribute rowspan but we need to know how many records will be grouped in each Account. For this we could use a controller that allow us order and manage the records that will be shown in the Visualforce page.
 
Here is an example that I made for the visualforce and Apex Code. 

<apex:page standardController="Contact" extensions="contactsByAccount_extension" >

  <table border="1" cellspacing="0" width="60%">
    <thead>
        <th> Account </th>
        <th> Title </th>
        <th> Name </th>
        <th> Email </th>
    </thead>
       
    <apex:repeat value="{!contactsByAccount}" var="key" >
      
      <apex:repeat value="{!contactsByAccount[key].contactList}" var="keyvalue" > 
        <tr> 
           <td rowspan="{!contactsByAccount[key].numOfContacts}" style="display:{!IF(CASESAFEID(keyvalue.id)==CASESAFEID(contactsByAccount[key].firstOfList), 'table-data','none' )};"> {!keyvalue.Account.name} </td>
          <td> {!keyvalue.Title} </td>
          <td> {!keyvalue.Name} </td>
          <td> {!keyvalue.Email} </td>  
        </tr>
      </apex:repeat>
      
    </apex:repeat> 
  </table>
    
</apex:page>

In the Visualforce page we iterate through a Map that comes from the extension controller. This Map group the records by Account. So for each Account we also iterate through the contacts of that Account.

The first cell, that show the name of the Account ONLY should be displayed in the first iteration, and it's not recomended to use apex variables in an apex:repeat, so a possible solution for this is making a method in the controller that let us know if the record is the first of the list. Thus we use in the style attribute of the <td> tag a conditional for display the cell. If the record ID of the current record is equal to the first of the list then display the account name. The rest of the cells are displayed without condition. 

Let's see the extension controller now.

public class ContactsByAccount_extension {

    public ContactsByAccount_extension(ApexPages.StandardController controller) {

    }
    
    public Map<String,contactosListWrapper> getContactsByAccount(){
    
      List<Contact> result = [SELECT Account.name, Title, Name, Email FROM Contact ];
    
      // Group Contacts by Account                                
      Map<String,contactosListWrapper> contactsByAccount = new Map<String,contactosListWrapper>();
      for(Contact cont: result){
        if(null == cont.Account.name) continue;
        contactosListWrapper empresa = contactsByAccount.get(cont.Account.name);
        if(null == empresa){
            contactsByAccount.put(cont.Account.name, new contactosListWrapper(new List<contact>()) );    
        }
        contactsByAccount.get(cont.Account.name).contactList.add(cont);
      }
      
      return contactsByAccount;
    }
    
   // List of contacts and details  
   class contactosListWrapper {
       
       public List<Contact> contactList {get; set;}
       
       public Integer numOfContacts {
          get{
            return contactList.size();
          }
          set;
       }
       
       public Id firstOfList{
          get{
            return contactList[0].Id;
          }
          set;
       }
             
       public contactosListWrapper(List<contact> listContacts){
           contactList = listContacts;
           
       }

The inner class contactosListWrapper is a container of a contact list and give us information about wich is the first and how many of them are. 

The extension controller method getContactsByAccount() makes a query of all the contacts and then they are group in the map by Account. For each Account we make a new entry in the Map with the name of the Account (String) and the list of contacts (contactosListWrapper). 

Sometimes we need to build a table for showing grouped records by a particular field. Here I show one way to accomplish this. 

For example, suppose we want to show all contacts grouped by company as shown in the following image.

Table of Contacts grouped by Account

In order to group cells in a html table we could use the attribute rowspan but we need to know how many records will be grouped in each Account. For this we could use a controller that allow us order and manage the records that will be shown in the Visualforce page.
 
Here is an example that I made for the visualforce and Apex Code. 

<apex:page standardController="Contact" extensions="contactsByAccount_extension" >

  <table border="1" cellspacing="0" width="60%">
    <thead>
        <th> Account </th>
        <th> Title </th>
        <th> Name </th>
        <th> Email </th>
    </thead>
       
    <apex:repeat value="{!contactsByAccount}" var="key" >
      
      <apex:repeat value="{!contactsByAccount[key].contactList}" var="keyvalue" > 
        <tr> 
           <td rowspan="{!contactsByAccount[key].numOfContacts}" style="display:{!IF(CASESAFEID(keyvalue.id)==CASESAFEID(contactsByAccount[key].firstOfList), 'table-data','none' )};"> {!keyvalue.Account.name} </td>
          <td> {!keyvalue.Title} </td>
          <td> {!keyvalue.Name} </td>
          <td> {!keyvalue.Email} </td>  
        </tr>
      </apex:repeat>
      
    </apex:repeat> 
  </table>
    
</apex:page>

In the Visualforce page we iterate through a Map that comes from the extension controller. This Map group the records by Account. So for each Account we also iterate through the contacts of that Account.

The first cell, that show the name of the Account ONLY should be displayed in the first iteration, and it's not recomended to use apex variables in an apex:repeat, so a possible solution for this is making a method in the controller that let us know if the record is the first of the list. Thus we use in the style attribute of the <td> tag a conditional for display the cell. If the record ID of the current record is equal to the first of the list then display the account name. The rest of the cells are displayed without condition. 

Let's see the extension controller now.

public class ContactsByAccount_extension {

    public ContactsByAccount_extension(ApexPages.StandardController controller) {

    }
    
    public Map<String,contactosListWrapper> getContactsByAccount(){
    
      List<Contact> result = [SELECT Account.name, Title, Name, Email FROM Contact ];
    
      // Group Contacts by Account                                
      Map<String,contactosListWrapper> contactsByAccount = new Map<String,contactosListWrapper>();
      for(Contact cont: result){
        if(null == cont.Account.name) continue;
        contactosListWrapper empresa = contactsByAccount.get(cont.Account.name);
        if(null == empresa){
            contactsByAccount.put(cont.Account.name, new contactosListWrapper(new List<contact>()) );    
        }
        contactsByAccount.get(cont.Account.name).contactList.add(cont);
      }
      
      return contactsByAccount;
    }
    
   // List of contacts and details  
   class contactosListWrapper {
       
       public List<Contact> contactList {get; set;}
       
       public Integer numOfContacts {
          get{
            return contactList.size();
          }
          set;
       }
       
       public Id firstOfList{
          get{
            return contactList[0].Id;
          }
          set;
       }
             
       public contactosListWrapper(List<contact> listContacts){
           contactList = listContacts;
           
       }

The inner class contactosListWrapper is a container of a contact list and give us information about wich is the first and how many of them are. 

The extension controller method getContactsByAccount() makes a query of all the contacts and then they are group in the map by Account. For each Account we make a new entry in the Map with the name of the Account (String) and the list of contacts (contactosListWrapper).