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
Jeff_Rogers.ax1383Jeff_Rogers.ax1383 

Creating a Visualforce Email Template to Display fields from Multiple Objects

I have 3 different objects that I want to display data from in anemail template: Case -> Case Product -> Problem

 

I want to send the email from the case page and want to display a table of Case Products and associated Problems.

 

Is this possible?  If so, what would be the plan of attack?

 

I would think I'd need a component in the visualforce email template that would display data aggregated via a wrapper class.

 

I'm able to get this to work on a standard visualforce page with a commandlink to initiate a wrapper controller method that compiles the data for me (see the code below), but I don't know how to initiate that action through visualforce within an email template.  Suggestions?

 

Thanks!

 

Visualforce Page  - "TestPage"

<apex:page title="Wrapper Demo" controller="wrapperCON" tabStyle="Case">
    <apex:sectionHeader title="Wrapper Demo"/>
    <apex:form >
    <apex:pageBlock title="Setup">
        <apex:pageBlockButtons location="top">
            <apex:commandLink action="{!buildwrapper}">
                <apex:param name="caseId" value="500Q0000003woSF"/>  
                       <apex:commandbutton value="Demo" />
            </apex:commandLink> 
        </apex:pageBlockButtons>
    </apex:pageBlock>
    <apex:pageBlock >
        <apex:repeat value="{!wrapout}" var="w">
            <hr/>
           <apex:outputText value="{!w.caseProd.name}" /> <br/>
                   <apex:repeat value="{!w.problems}" var="wo">
                       Problems: <apex:outputText value="{!wo.Prob.name}" /> <br/>
                   </apex:repeat>
        </apex:repeat>
    </apex:pageBlock>
    </apex:form>
</apex:page>

 

//APEX CONTROLLER 

public class wrapperCON {
       
    private List<Case_Product__c> tempCaseProd = new List<Case_Product__c>();
    private Map<ID,List<Problem__C>> CaseProdProbMAP = new Map<ID,List<Problem__c>>();
    private Set<ID> CaseProdIds = new Set<ID>();//case product ids to get problems from
    public List<wrapper> wrapout {get; set;}
    //constructor
    public wrapperCON(){
       wrapout = new List<wrapper>();
       
    }    
    //wrapper 1
    class wrapper{
        public Case_Product__c caseProd {get; set;}
        //This is a list of other wrappers that is nested in the first list
        public List<wrapper2> problems {get; set;}
        public wrapper(){
            if(caseProd==null){caseProd = new Case_Product__c();}//initialize the case product holder
            if(problems==null){problems = new List<wrapper2>();}//initialize the wrapper listholder
        }
    }
    //wrapper 2 - the sub-wrapper
    class wrapper2{
        public Problem__c Prob {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() {
        String caseId = ApexPages.currentPage().getParameters().get('caseId');//'500Q0000003woSF';
        
   
        
        String queryString = 'select id,name from Case_Product__c where case__c IN (\'' + caseId + '\')';
        tempCaseProd = database.query(queryString);
        system.debug(tempCaseProd);
        for(Case_Product__c cp:tempCaseProd){CaseProdIds.add(cp.id);}
        for(Problem__c p:[select id,name,case_product__c from Problem__c where Case_Product__c=:CaseProdIds]){
            if(CaseProdProbMap.containsKey(p.case_product__c)){
                CaseProdProbMap.get(p.case_product__c).add(p);//adds problems for this case product to the problems list in the map    
            }else{
                CaseProdProbMap.put(p.case_product__c,new List<Problem__C>{p});//adds new problem list for this case product to the map    
            }
        }
        for(Case_Product__c cp2:tempCaseProd){
            wrapper tmpwrapper = new wrapper();
            tmpwrapper.caseProd=cp2;
            List<wrapper2> t2 = new List<wrapper2>();
            for(Problem__c pp:CaseProdProbMap.get(cp2.id)){
                wrapper2 twrap2 = new wrapper2();
                twrap2.Prob=pp;
                t2.add(twrap2);
            }
            tmpwrapper.problems=t2;
            wrapout.add(tmpwrapper);
        }


        return null;
    }

    
}

 

liron169liron169

You indeed need component & controller behine it.

In those cases that I need data from several objects I would rather center them in
one components, instead of creating component for each object.

you can do controller like this:

private Producy__c product =new Prodeuct();
private List<Case_Product__c> tempCaseProd = new List<Case_Product__c>();
private Map<ID,List<Problem__C>> CaseProdProbMAP = new Map<ID,List<Problem__c>>();
private String dummy='';

public getDummy()
{
    
    //code for initialize all data
    //1.product
    //2.case_product list
    //3.case_product_problem map

    return dummy;
}

public Producy__c getProduct()
{
    return product;
}

public List<Case_Product__c> getTempCaseProd()
{
    return tempCaseProd ;
}

public Map<ID,List<Problem__C>> getCaseProdProbMap()
{
    retrurn CaseProdProbMAP ;
}



After that in compoent call first the dummy variable.
And then call all the other objects.
something like:


{!dummy}
<apex:repeat value="{!tempCaseProd}" var="caseProd" id="caseID">
    <apex:outputText value="{!caseProd.name}" /> <br/>
</apex:repeat>