I have a visualforce component which is used in an email template to display a list of related items.

Visualforce Component

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

    <apex:attribute name="parentId" type="Id" assignTo="{!leadId}" description="" />
    <apex:attribute name="products" type="Lead_Product__c[]" description="" />

    <table style="width:500px" cellpadding="3" cellspacing="0" border="0">
        <thead style="background-color: #003366; color: #ffffff;">
            <td>Sales Price</td>
            <td>Total Price</td>
            <apex:repeat value="{!products}" var="p">
                    <td>{!p.Product_Code__c }</td>


Apex Controller

public without sharing class LeadProductsController {

    public List<Lead_Product__c> products { get; private set; }

    public String leadId;

    public void setLeadId(Id leadId) {
        this.leadId = leadId;
        products = [SELECT Id, Product_Name__c, Product_Code__c, Quantity__c FROM Lead_Product__c WHERE Lead__c = :leadId];

    public Id getLeadId() {
        return leadId;

I then use the component in the email template like this:

<c:LeadProducts parentId="{!relatedTo.Id}" ></c:LeadProducts>

I've not used visualforce component's before, so I suspect I am doing many things wrong, but I cannot find a decent example of what I am trying to achieve.

How should I get a List of data from a controller when using a visualforce component?


You have three glaring problems which don't have anything to do with the error:

  1. Your attribute name and assignTo use the same name.
  2. Your query selects only Id.
    • Make sure you actually pull all the fields you need.
  3. You make your query in your constructor.
    • Data retrieval needs to move to your leadId setter.
    • The constructor fires before this setter and as currently written will use null as the filter value.

Now for the actual cause:

  1. Your products Apex variable is not an attribute.

    • Add { get; private set; } to the end of the line:

      public List<Lead_Product__c> products { get; private set; }
    • Always use a private setter unless you actually want the page to write to this variable.
  2. You have included products as a conflicting <apex:attribute>.

    • Simply remove this line from your markup:

      <apex:attribute name="products" type="List" description="" />
    • You set the type as List, which I assume implicitly converts to Object[].
    • If you needed this <apex:attribute>, you would change the type to Lead_Product__c[]
  • Thank you! that was super helpful. I was able to deploy my code. Alas, nothing actually appears in the email. I've updated the code example in the question. any ideas what might be the issue now? – Robs Jan 23 at 22:48
  • 1
    Take a closer look at what I recommend for fix 2. @Robs – Adrian Larson Jan 23 at 22:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.