Use Case |
We need to have specific design upfront on how we can prepare the data and it will dynamically consider rowspan and format the rows accordingly in Visualforce.
For the sake of simplicity I am using Salesforce Contact record to prepare and display those.
Approach in Apex Controller to prepare data
1) Create a ContactWrapper with all the Contact attributes.
2) In
getData()
method, retrieve the Contact List firing SOQL query and prepare the desired map i.e. mapModule which contains ModuleName as Key and List<ContactWrapper>
. In that method, prepare a List of ModuleNames from mapModule.keySet()
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | public class ContactController{ //map to capture Module Name and List of Contacts public Map<String, List<ContactWrapper>> mapModule {get;set;} //map to capture Module Name and count of Contact which will be used to define rowspan. public Map<String, Integer> moduleCountMap{get;set;} //to capture list of Module Names public List<String> moduleList {get;set;} public ContactController() { mapModule = new Map<String, List<ContactWrapper>>(); moduleCountMap = new Map<String, Integer>(); moduleList = new List<String>(); } //prepare data to display in Visualforce. public void getData() { List<Contact> contactList = [SELECT Name, Phone, Email, Module__c FROM Contact WHERE Module__c !=null ORDER BY Module__C LIMIT 10]; //grouped into module and prepare the map for(Contact conObj:contactList) { List<ContactWrapper> conWrapperList = new List<ContactWrapper>(); //verify map already contains same Module Name if(mapModule.containsKey(conObj.Module__c)) { //retrieve the list of existing contact conWrapperList = mapModule.get(conObj.Module__c); //put the new contact to the list conWrapperList.add(new ContactWrapper(conObj)); mapModule.put(conObj.Module__c, conWrapperList); //store count of rows per Module Name modulecountMap.put(conObj.Module__c, conWrapperList.size()); } else { //create a new map of Module Name conWrapperList.add(new ContactWrapper(conObj)); mapModule.put(conObj.Module__c, conWrapperList); //store count of rows per Module Name modulecountMap.put(conObj.Module__c, conWrapperList.size()); } } //create a list of Module Names which will be helpful to iterate moduleList = new List<String>(mapModule.keySet()); } public Class ContactWrapper { public ContactWrapper(Contact conObj) { this.Name = conObj.Name; this.Phone = conObj.Phone; this.Email = conObj.Email; this.Module = conObj.Module__c; } public String Name {get;set;} public String Phone {get;set;} public String Email {get;set;} public String Module {get;set;} } } |
Approach in Visualforce to display data
1) First, repeat through
moduleList
which contains all the Module Name as key.
2) To determine,
td rowspan
get the size by {!modulecountMap[key]}
.
3) Create an inner repeat which has List of ContactWrapper which we can easily retrieve by
{!mapModule[key]}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <apex:page controller="ContactController" action="{!getData}"> <table border="1"> <tr> <th>Group</th> <th>Name</th> <th>Phone</th> <th>Email</th> </tr> <apex:repeat id="myRepeatHeader" value="{!moduleList}" var="key"> <apex:variable var="count" value="{!1}"/> <apex:repeat id="contactDetails" value="{!mapModule[key]}" var="att"> <tr> <apex:outputPanel layout="none" rendered="{!count==1}"> <td rowspan="{!(modulecountMap[key])}"><apex:outputText value="{!att.Module}"/></td> </apex:outputPanel> <td><apex:outputText value="{!att.Name}"/></td> <td><apex:outputText value="{!att.Phone}"/></td> <td><apex:outputText value="{!att.Email}"/></td> <apex:variable var="count" value="{!count+1}"/> </tr> </apex:repeat> </apex:repeat> </table> </apex:page> |
At my DE, it is showing like this.
To get a help on how to use rowspan in HTML, refer td row span
Conclusion
Overall, it's look like a easy to implement functionality but until and unless we make our hands dirty in writing logic, we cant release whats the pain in developing this.
But, it's a charm, working on this type of requirements.