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
Reppin__505Reppin__505 

Visual Force Page using Wrapper Class to nest multiple objects in repeats

Hello all,

 

I have a requirement for a visualforce page which would repeat tables of account related information. Now i can do this easily if its just an object with one single related object. However if i have for example an Account, then a related object, then an object related to that related object i find that it becomes a little more challenging because soql can only return 1 inner object. 

 

So, i know a wrapper class is the suggested resolution to this problem. However they are very poorly documented and i cannot 'wrapper' my head around them. lol terrible i know. For instance i don't know how i can build a list with all of these related objects to use in the visual force page.

 

So if someone could please walk me (and i'm sure countless others) through this process that would be greatly greatly appreciated. 

 

Here is what i'm trying to do:

 

Relating multiple=

 



JimRaeJimRae

A wrapper class does not need to be a daunting item.  Think of them as "virtual tables" to hold a collection of data that you need to have in a list.  The special thing about them is that as a virtual table, they can hold data combined from different sources, which provides a lot of flexibility. They also can do calculations and other processing on records or fields as they are added  before they get exposed back as an output list.  Lastly, they can hold nested table elements or even other sObject items for a more powerful experience.

 

The way you use a wrapper class is like this:

In your apex class (this could be a trigger, could be controller or extension for a VF page) you create an internal class. That class will have properties (how your data gets back out) and a constructor (how your data gets in).  You can then create a list of your wrapper objects, and then add items to the list by using the "new" keyword and passing in your source data.

 

Here is some pseudo code to get you started (should work, but doesn't go all the way to the 3rd level of nesting):

 

//VF PAGE
<apex:page title="Wrapper Demo" controller="wrapperCON" tabStyle="Account">
    <apex:sectionHeader title="Wrapper Demo"/>
    <apex:form>
    <apex:pageBlock title="Setup">
        <apex:pageBlockButtons location="top">
            <apex:commandButton value="Run Demo" action="{!buildwrapper}"/>
        </apex:pageBlockButtons>
    </apex:pageBlock>
    <apex:pageBlock>
        <apex:repeat value="{!wrapout}" var="w">
            <hr/>
           <apex:outputText value="{!w.acct.name}" /> <br/>
                   <apex:repeat value="{!w.opps}" var="wo">
                       OPPORTUNITY: <apex:outputText value="{!wo.Opp.name}" /> <br/>
                   </apex:repeat>
        </apex:repeat>
    </apex:pageBlock>
    </apex:form>
</apex:page>

 

//APEX CONTROLLER
public class wrapperCON {
    private List<Account> tempAcct = new List<Account>();
    private Map<ID,List<Opportunity>> AcctOppMAP = new Map<ID,List<Opportunity>>();
    private Set<ID> AcctIDs = new Set<ID>();//account ids to get opportunities from
    public List<wrapper> wrapout {get; set;}
    //constructor
    public wrapperCON(){
       wrapout = new List<wrapper>();
    }    
    //wrapper 1
    class wrapper{
        public Account acct {get; set;}
        //This is a list of other wrappers that is nested in the first list
public List<wrapper2> opps {get; set;} public wrapper(){ if(acct==null){acct = new Account();}//initialize the account holder if(opps==null){opps = new List<wrapper2>();}//initialize the wrapper listholder } } //wrapper 2 - the sub-wrapper class wrapper2{ public Opportunity Opp {get; set;} public List<Product2> oppprods {get; set;} public wrapper2(){ if(Opp==null){Opp = new Opportunity();}//initialize the Opportunity holder if(oppprods==null){oppprods = new List<Product2>();}//initialize the product2 holder } } //This is the "Run Demo" Button on our VF page public PageReference buildwrapper() { tempAcct = [select id,name from Account where id in (select accountid from Opportunity) limit 10]; for(Account a:tempAcct){AcctIDs.add(a.id);} for(Opportunity o:[select id,name,accountid from Opportunity where Accountid=:AcctIDs]){ if(AcctOppMap.containsKey(o.accountid)){ AcctOppMap.get(o.Accountid).add(o);//adds opportunity for this account to the opp list in the map }else{ AcctOppMap.put(o.Accountid,new List<Opportunity>{o});//adds new opportunity list for this account to the map } } for(Account aa:tempAcct){ wrapper tmpwrapper = new wrapper(); tmpwrapper.acct=aa; List<wrapper2> t2 = new List<wrapper2>(); for(Opportunity oo:AcctOppMap.get(aa.id)){ wrapper2 twrap2 = new wrapper2(); twrap2.Opp=oo; t2.add(twrap2); } tmpwrapper.opps=t2; wrapout.add(tmpwrapper); } return null; } }

 

 

 

 

Reppin__505Reppin__505

Wow, thanks for that clear explanation. This will help a ton and i'll try and test the code you supplied when i get a chance. Thanks again.

 

Antonio

Pradeep_NavatarPradeep_Navatar

You can do this through wrapper class and bind the data variables through wrapper class variables.

 

Find below a sample code to get the records through Wrapper class :

 

VF CODE:
<apex:repeat value="{!ListConnection}" var="conn">
<apex:outputText value="{!conn.PropName}">
<apex:outputText value="{!conn.PropEmail}">
</apex:outputText>

Controller code:
public List<WrapperConnection> lstcon = new List<WrapperConnection>{};
Public List<String> ListConnection {get { return lstcon; } set { lstcon = value; }}
List<Connection__c> connconname = new List<Connection__c> ();
connconname =[Select Id,Name,Status__c,Role__c,Contact__c,Contact__r.Name,Contact__r.Email from  Connection__c where Occurrence__c =:aid and Role__c ='Volunteer Leader'];
for(integer i=0; i<connconname.size(); i++){
WrapperConnection wrapcon = new WrapperConnection();
wrapcon.PropName = connconname[i].Contact__r.Name;
wrapcon.PropEmail = connconname[i].Contact__r.Email;
lstcon.add(wrapcon);
}
public class WrapperConnection
{
public String PropName{ get; set; }
public String PropEmail{ get; set; }
}//End Class WrapperConnection

Reppin__505Reppin__505

This is taking me forever to figure out how to get the 3rd level of nesting into the master wrapper. Any chance someone can show me how to insert the 3rd object so that i can reference it in my visual force page?

Reppin__505Reppin__505

What i don't understand is: 

 

Does there need to be two new wrapper classes each time a new object is going to be appended to the list? For instance if i want to make a wrapper class which includes Account, Contact, Opportunities How many of the following:

 

    //wrapper 1
    class wrapper{
        public Account acct {get; set;}
        //This is a list of other wrappers that is nested in the first list
public List<wrapper2> opps {get; set;} public wrapper(){ if(acct==null){acct = new Account();}//initialize the account holder if(opps==null){opps = new List<wrapper2>();}//initialize the wrapper listholder } } //wrapper 2 - the sub-wrapper class wrapper2{ public Opportunity Opp {get; set;} public List<Product2> oppprods {get; set;} public wrapper2(){ if(Opp==null){Opp = new Opportunity();}//initialize the Opportunity holder if(oppprods==null){oppprods = new List<Product2>();}//initialize the product2 holder } }

 

Need to be created?

 

And then once i have a class for all of my objects and i build the lists through the loops like you have done here:

 

        tempAcct = [select id,name from Account where id in (select accountid from Opportunity) limit 10];
        for(Account a:tempAcct){AcctIDs.add(a.id);}
        for(Opportunity o:[select id,name,accountid from Opportunity where Accountid=:AcctIDs]){
            if(AcctOppMap.containsKey(o.accountid)){
                AcctOppMap.get(o.Accountid).add(o);//adds opportunity for this account to the opp list in the map    
            }else{
                AcctOppMap.put(o.Accountid,new List<Opportunity>{o});//adds new opportunity list for this account to the map    
            }
        }

How do i then group the objects together? I kind of see how you did it with the Account and the Opportunity in the WrapOut list, but i don't entirely understand what's going on in that final loop. Can someone please elaborate what is going on here please? This final loop is where i'm really getting lost.

 

        for(Account aa:tempAcct){
            wrapper tmpwrapper = new wrapper();
            tmpwrapper.acct=aa;
            List<wrapper2> t2 = new List<wrapper2>();
            for(Opportunity oo:AcctOppMap.get(aa.id)){
                wrapper2 twrap2 = new wrapper2();
                twrap2.Opp=oo;
                t2.add(twrap2);
            }
            tmpwrapper.opps=t2;
            wrapout.add(tmpwrapper);
        }

 

Any help would be greatly appreciated. Knowing how to add the third object is the most important and would allow me to add a fourth or fifth which could have so many applications. Thanks.

Reppin__505Reppin__505

Never mind, i got it!!

JennaBJennaB

Could you post your solution for getting 3rd level? This would be useful to know. Thank you.

shuchi_dhallshuchi_dhall

pls share an example to save data using wrapper class.

 

The below code gives an exception "System.list exception;duplicate id in list"

I am selecting only one profile and updating it, not sure where i am encountering duplicacy.

 

Highly appreciate your help

 

public List<ProfWrapper> getSelectedprofiles() {
   
        nextAction();
          try        
          {           
              if(lProfWrappers1==null)
              {
                  lProfiles =[selectName,Profile_Name__c,Description__c,API_Enabled__c,API_Only_User__c,Author_Apex__c,Bulk_API_Hard_Delete__c,Create_and_Upload_Change_Sets__c,Customize_Application__c,Deploy_Change_Sets__c,Edit_HTML_Templates__c,Edit_Read_Only_Fields__c,IP_Restrict_Requests__c,Manage_Analytic_Snapshots__c,Manage_Billing__c,Manage_Business_Hours_Holidays__c,Manage_Call_Centers__c,Manage_Categories__c,Manage_Custom_Report_Types__c,Manage_Dashboards__c,Manage_Data_Categories__c,Manage_Data_Integrations__c,Manage_Dynamics_Dashboards__c,Manage_Email_Client_Configurations__c,Manage_Letterheads__c,Manage_Mobile_Configurations__c,Manage_Package_Licenses__c,Manage_Public_Documents__c,Manage_Public_List_Views__c,Manage_Public_Reports__c,Manage_Public_Templates__c,Manage_Salesforce_CRM_Content__c,Manage_Translation__c,Manage_Users__c,Modify_All_Data__c,Password_Never_Expires__c,Permit_User_to_grant_login_access_to_sal__c,Reset_User_Passwords_and_Unlock_Users__c,Schedule_Dashboards__c,Schedule_Reports__c,Send_Outbound_Messages__c,Transfer_Record__c,Use_Team_Reassignment_Wizards__c,View_All_Data__c,View_Data_Categories__c,View_Setup_and_Configuration__c,Weekly_Data_Export__c,Activate_Contracts__c,Approve_Contracts__c,Convert_Leads__c,Create_and_Customize_Reports__c,Create_AppExchange_Packages__c,Create_Libraries__c,Delete_Activated_Contracts__c,Deliver_Uploaded_Files_and_Personal_Cont__c,Download_AppExchange_Packages__c,Drag_and_Drop_Dashboard_Builder__c,Edit_Case_Comments__c,Edit_Events__c,Edit_Opportunity_Product_Sales_Price__c,Edit_Self_Service_Users__c,Edit_Tasks__c,Export_Reports__c,Import_Leads__c,Import_Personnal_Contacts__c,Import_Solutions__c,Insert_System_Field_Values_for_Chatter_F__c,Manage_Cases__c,Manage_Content_Permissions__c,Manage_Content_Properties__c,Manage_Content_Type__c,Manage_Leads__c,Manage_Published_Solutions__c,Manage_Remote_Access__c,Manage_Self_Service_Portal__c,Mass_Edit_from_Lists__c,Mass_Email__c,Report_Builder__c,Run_Reports__c,Send_Email__c,Send_Stay_in_Touch_Requests__c,Transfer_Cases__c,Transfer_Leads__c,Upload_AppExchange_Packages__c,View_My_Team_s_Dashboards__c,Connect_for_Lotus_Notes__c,Connect_for_Microsoft_Outlook__c,Connect_for_Office__c,Desktop_Integration_Client_OffLine__c from Profile__c where id in :lSelectedProfileIds];
                  if(!lProfiles.isEmpty())           
                  {               
                      lProfWrappers1 = new List<ProfWrapper>();               
                      for(integer i=0; i < lProfiles.size(); i++)                                 
                      {                   
                          ProfWrapper wrapperRec = new ProfWrapper(lProfiles[i]);                   
                          lProfWrappers1.add(wrapperRec);               
                      }           
                  }
             }      
         } catch (System.QueryException ex)       
         {                        
             ApexPages.addMessages(ex);        
         }       
        return lProfWrappers1;   
   
    }

public pageReference UpdateData()
    {
       for(ProfWrapper prwrapper : lProfWrappers1)
         lProfiles.add(prwrapper.profVar1); 
         if(lProfiles.size()>0) 
         {
             update lProfiles;                        
         }
         else
         {

         } 

        return null;
   
    }

Jeff_Rogers.ax1383Jeff_Rogers.ax1383

Reppin__505,


Would you be kind and share the code as an example on how to display data from the third object? using vf and a wrapper class.  Thank you!

 

Jeff

rajesh k 10rajesh k 10
Hi,
Using schema methods and wrapper class i displayed all custom objects in an visualforce page and also i gave each custom object as edit and delete command links.My requirement is when i click a command object related delete custom link automatically that command delete link related custom object is delete from database how?

please help me...............
Shivani Desai 19Shivani Desai 19
Hello All,
Am daunted by the concept of wrapper class as am trying to render two tables in a visualforce page from a custom controller using two different wrapper returns. Am able to render data in the first table but unable to do so for the second table. Not sure what I am doing wrong. 

Here is my controller:
public with sharing class SL_PerformanceDashboardController {
  private SL_PerformanceDataWrapper.cls_employees employee {get;set;}
  private SL_PerformanceDataWrapper.cls_employees employed {get;set;}
  public String employeeId {get;set;}
  public String employeeName {get;set;}
  public Date today {get;set;}
  public List<EmployeeWrapper> employeeMap {get;set;}
  public Integer year {get;set;}
  public Boolean displayEmployeeChoices {get;set;}
  public List<SelectOption> yearOptions {get;set;}
  public Map<String, String> yearToAsterisk {get;set;}
  

  public SL_PerformanceDashboardController() {
    today = System.today();
    year = today.year();
    yearToAsterisk = new Map<String, String>();
    for (Performance_Dashboard_Footnote__c note: Performance_Dashboard_Footnote__c.getall().values()){
      yearToAsterisk.put(note.Year__c, '* ' + note.Display_Text__c);
    }

    yearOptions = new List<SelectOption>{new SelectOption(String.valueOf(year), String.valueOf(year)), new SelectOption(String.valueOf(year-1), String.valueOf(year-1))};
    employeeId = ApexPages.currentPage().getParameters().get('id');

    // should display current running user's info if it exists
    if (employeeId == null){
      List<Contact> employeeContact = [SELECT TitanAEUserId__c FROM Contact WHERE Employee_User_Lookup__c = :UserInfo.getUserId()];
      if (!employeeContact.isEmpty()){
        employeeId = employeeContact[0].TitanAEUserId__c;
      }
      // default to Amanda Rosenberg for now
      if (employeeId == null || employeeId == 'sladmin'){
        employeeId = '';
      }      
    }

    Boolean admin = UserInfo.getProfileId() == [SELECT Id FROM Profile WHERE Name = 'System Administrator'].Id;

    Set<String> validEmployeesToDisplay = new Set<String>();

    if (!admin){
      for (Contact employee: SL_RoleHelper.getEmployeeContactsForRunningUser()){
        validEmployeesToDisplay.add(employee.TitanAEUserId__c);
      }    

      validEmployeesToDisplay.remove('null');
    }

    employeeMap = new List<EmployeeWrapper>();
    Set<String> uniqueNames = new Set<String>();
    for (SL_PerformanceDataWrapper.cls_employees employee: SL_RetrievePerformanceData.retrieveData().employees){
      // this employeename retrieve may break for employees without incentiveperformance lists
      String name = !employee.incentiveperformance.isEmpty() ? employee.incentiveperformance[0].employeename != '' ? employee.incentiveperformance[0].employeename : '' : '';
      if ((admin || validEmployeesToDisplay.contains(employee.employeeid)) && !uniqueNames.contains(name)){
        employeeId = employee.employeeid;
        uniqueNames.add(name);
        employeeMap.add(new EmployeeWrapper(name, employee.employeeid));
      }
     }
     
    // only display AE selection if the user has options
    displayEmployeeChoices = employeeMap.size() > 1;
    retrieveData();
    
  }

  public String getEmployeeNames(){
    return JSON.serialize(employeeMap);
  }

  public class EmployeeWrapper {
    public String name {get;set;}
    public String id {get;set;}

    public EmployeeWrapper(String name, String id){
      this.name = name;
      this.id = id;
    }
  }

  public void retrieveData(){
    for (SL_PerformanceDataWrapper.cls_employees employee: SL_RetrievePerformanceData.retrieveData().employees){
      if ((employee.employeeid+'|'+employee.year).equals(employeeId+'|'+this.year)){
        this.employee = employee;
        this.employeeName = !employee.incentiveperformance.isEmpty() ? 
                  employee.incentiveperformance[0].employeename : 
                  '';
        return;
      }
    }
    return;
  }
  
   
  

  public class BudgetSummaryWrapper {
    public List<LineItemWrapper> lineItems {get;set;}
    
    public String header {get;set;}
   
    public BudgetSummaryWrapper(List<SL_PerformanceDataWrapper.cls_incentiveperformance> lineItems, String header){
      this.lineItems = new List<LineItemWrapper>();
      Boolean color = true;
     
      for (SL_PerformanceDataWrapper.cls_incentiveperformance lineItem: lineItems){
        this.lineItems.add(new LineItemWrapper(lineItem, color));
        color = !color;
        
      }
      this.header = header;
    }
  }

  public class LineItemWrapper{
    public SL_PerformanceDataWrapper.cls_incentiveperformance lineItem {get;set;}
    
    public Boolean color {get;set;}
    public LineItemWrapper(SL_PerformanceDataWrapper.cls_incentiveperformance lineItem, Boolean color){
      this.lineItem = lineItem;
      this.color = color;
     
    }
  }

  public List<BudgetSummaryWrapper> getBudgetSummary(){
    List<BudgetSummaryWrapper> wrappers = new List<BudgetSummaryWrapper>();
    Map<String, List<SL_PerformanceDataWrapper.cls_incentiveperformance>> budgetTypeToLineItems = new Map<String, List<SL_PerformanceDataWrapper.cls_incentiveperformance>>();
    // group incentiveperformance records by type
    if (employee != null && employee.incentiveperformance != null){
      for (SL_PerformanceDataWrapper.cls_incentiveperformance lineItem: employee.incentiveperformance){
        
        String typeKey = lineItem.type;
        //String CheckOne = lineItem.description;
        // production and quarterly are grouped
        
        if (typeKey == 'Production' || typeKey == 'Quarterly'){
          typeKey = 'Personal';
         }

        if (budgetTypeToLineItems.containsKey(typeKey)){
          budgetTypeToLineItems.get(typeKey).add(lineItem);
        }
        else {
          budgetTypeToLineItems.put(typeKey, new List<SL_PerformanceDataWrapper.cls_incentiveperformance>{lineItem});
        }
       
      
    }
      // create wrappers
      for (String key: budgetTypeToLineItems.keySet()){
        wrappers.add(new BudgetSummaryWrapper(budgetTypeToLineItems.get(key), year + ' ' + key + ' Budget'));
      }
     }
   
    return wrappers;
  }
  
   public class newBudgetSummaryWrapper {
    public List<newLineItemWrapper> newlineItems {get;set;}
    
    public String newheader {get;set;}
   
    public newBudgetSummaryWrapper(List<SL_PerformanceDataWrapper.cls_newincentiveperformance> newlineItems, String newheader){
      this.newlineItems = new List<newLineItemWrapper>();
      Boolean newcolor = true;
     
      for (SL_PerformanceDataWrapper.cls_newincentiveperformance newlineItem: newlineItems){
        this.newlineItems.add(new newLineItemWrapper(newlineItem, newcolor));
        newcolor = !newcolor;
        
      }
      this.newheader = newheader;
    }
  }

  public class newLineItemWrapper{
    public SL_PerformanceDataWrapper.cls_newincentiveperformance newlineItem {get;set;}
    
    public Boolean newcolor {get;set;}
    public newLineItemWrapper(SL_PerformanceDataWrapper.cls_newincentiveperformance newlineItem, Boolean newcolor){
      this.newlineItem = newlineItem;
      this.newcolor = newcolor;
     
    }
  }
  public List<newBudgetSummaryWrapper> getnewBudgetSummary(){
    List<newBudgetSummaryWrapper> newwrappers = new List<newBudgetSummaryWrapper>();
    Map<String, List<SL_PerformanceDataWrapper.cls_newincentiveperformance>> newbudgetTypeToLineItems = new Map<String, List<SL_PerformanceDataWrapper.cls_newincentiveperformance>>();
    // group incentiveperformance records by type
     if (employee != null && employee.newincentiveperformance != null){
      for (SL_PerformanceDataWrapper.cls_newincentiveperformance newlineItem: employee.newincentiveperformance){
        
        String typeKey = newlineItem.newdescription;
        //String CheckOne = 
        // production and quarterly are grouped
        
   
          newbudgetTypeToLineItems.put(typeKey, new List<SL_PerformanceDataWrapper.cls_newincentiveperformance>{newlineItem});
    
      
    }
      // create wrappers
      for (String key: newbudgetTypeToLineItems.keySet()){
        newwrappers.add(new newBudgetSummaryWrapper(newbudgetTypeToLineItems.get(key), year + ' ' + key + ' Budget'));
      }
    }
   
    return newWrappers;
  }

    public List<Data> getData() {
        return getChartData();
    }

    public List<Data> getYearlyData() {
        return getYearlyChartData();
    }

    public List<Data> getYearlyChartData() {
        List<Data> data = new List<Data>();
        if (employee != null){
          data.add(new Data('Media', employee.totalmediarev, employee.totalsdlymediarev));
          data.add(new Data('Production', employee.totalproductionrev, employee.totalsdlyproductionrev));
        }

        return data;
    }

    public List<Data> getChartData() {
        List<Data> data = new List<Data>();
        if (employee != null){
          data.add(new Data('Jan', employee.totaljanrev, employee.totalsdlyjanrev));
          data.add(new Data('Feb', employee.totalfebrev, employee.totalsdlyfebrev));
          data.add(new Data('Mar', employee.totalmarrev, employee.totalsdlymarrev));
          data.add(new Data('Apr', employee.totalaprrev, employee.totalsdlyaprrev));
          data.add(new Data('May', employee.totalmayrev, employee.totalsdlymayrev));
          data.add(new Data('Jun', employee.totaljunrev, employee.totalsdlyjunrev));
          data.add(new Data('Jul', employee.totaljulrev, employee.totalsdlyjulrev));
          data.add(new Data('Aug', employee.totalaugrev, employee.totalsdlyaugrev));
          data.add(new Data('Sep', employee.totalseprev, employee.totalsdlyseprev));
          data.add(new Data('Oct', employee.totaloctrev, employee.totalsdlyoctrev));
          data.add(new Data('Nov', employee.totalnovrev, employee.totalsdlynovrev));
          data.add(new Data('Dec', employee.totaldecrev, employee.totalsdlydecrev));
        }

        return data;
    }

    public class Data {
        public String name { get; set; }
        public Double thisYear { get; set; }
        public Double sdly { get; set; }
        public Data(String name, Double thisYear, Double sdly) {
            this.name = name;
            this.thisYear = Math.round(thisYear);
            this.sdly = Math.round(sdly);
        }
    }
}