+ Start a Discussion
MJ09MJ09 

Using Custom Controllers within Visualforce Email Templates

The Visualforce developer's guide, p. 126, describes how to use a custom controller in the context of a VF email template by referencing a custom component from the email template. In the example in the doc, the custom component's controller does a query for all Acounts with a name like 'Smith%.' What if I'd like to pass the name (or any other value) into the component as an attribute -- or, in my case, pass in an ID that the controller should query for?

 

I've tried this:

<apex:component controller="Component_Controller" access="global">

<apex:attribute name="myId" type="String" description="The ID" assignTo="{!myId}" />

<pre>
myId = {!myId}

dbg = {!dbg}
</pre>

</apex:component>

My controller looks like this:

public class Component_Controller {

public String dbg {get;set;}
public String myId;

public Component_Controller() {
dbg = '';
dbg += '===== constructor: myId= ' + myId+ '\n';
}

public void setMyId(String str) {
dbg += '===== setmyId(' + str + ')\n';
myId= str;
}
}

 The email template refers to the component in the usual way:

<messaging:attachment renderAs="PDF" filename="Test">
<c:MyComponent MyId="{!RelatedTo.Opportunity__c}" />
</messaging:attachment>

When the email is generated, the attachment shows that the page has a valid value for MyId, but the controller never seems to get it. When the constructor adds to the dbg value, MyId is null. The dbg value shows that MyId's setter doesn't get called. 

 

I would add 'required="true"' to the attribute definition, but that's apparently not allowed if the component is defined with access="global," and global access is required for components called from an email template.

 

My bottom-line problem is that I need to send an email message from a trigger where the email contains a PDF with content that is so complex that I need a controller to help construct it.  I've created a regular VF page that contains the PDF, but I can't render the page's content from within a trigger -- a PageReference object's getContent() and getPDFContent() methods don't work from within a trigger (see Calling getContent() from a trigger). And I've tried using a simple PDF attachment without a custom component inside the template, but I can't get to the data I need (see this). I'm really stuck -- I'd appreciate any suggestions!

 

Thanks.

 

 

 

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
ciccic
Component controllers work a little differently to page controllers.  The contructor is called before setMyId so myId will be null.  If you move the code to calculate the value of dbg into a method called GetDbg() then it will work.

All Answers

ciccic
Component controllers work a little differently to page controllers.  The contructor is called before setMyId so myId will be null.  If you move the code to calculate the value of dbg into a method called GetDbg() then it will work.
This was selected as the best answer
JeriMorrisJeriMorris

Perfect! This did the trick.

 

I knew that the constructor was called before setMyId, but what I don't understand is why I had to do my work in the getter method, rather than the setter. When the component defines a variable with an assignTo attribute, I'd expect the setter to be called, but apparently it's the getter that gets called instead. What's the logic behind that?

 

Thanks for your help!