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?

1 Answer 1


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
    Commented Jan 23, 2019 at 22:48
  • 1
    Take a closer look at what I recommend for fix 2. @Robs
    – Adrian Larson
    Commented Jan 23, 2019 at 22:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .