[SalesForce] Pass Id from Visualforce Email template to Controller

I am trying to pass an id through a Visualforce email template component to its class.

The class creates a wrapper, to combine two objects records into one list. The two objects lists have a common ID (Account) which i am trying to pass through the Email Component…

<apex:attribute type="Id" name="recordId" description="Record ID" assignTo="{!recId}" />

I am having issue with the Order of Execution in page loads – I am aware that my controller is not reading the value, because the constructor is fired first.

I have looked at several posts which confirm this behaviour, and I have tried he suggestions, however I have not managed to solve this issue.

How do I pass the recId to the constructor?

UPDATE: The original pasted code was a snippet, I have added some more code/information to be more specific as to what I am trying to achieve.

APEX:

public class CustomerStatementTableEmailController {

    public Id recId {get;set;}

    public List <Delivery_Note__c> notDue {get;set;}  //less than 30
    public List <Delivery_Note__c> notDueC {get;set;}  //less than 30

    public class Wrapper implements Comparable {

    public Delivery_Note__c note {get; private set;}
    public Remittance_Advice__c remittance {get; private set;}
    public String dataType {get;private set;}

    public Wrapper(Delivery_Note__c n) {
        note = n;
        dataType = 'note';
    }

    public Wrapper(Remittance_Advice__c r) {
        remittance = r;
        dataType = 'remittance';
    }

    public Integer compareTo(Object instance){
        Wrapper that = (Wrapper)instance;
        if (this.calculatedDate > that.calculatedDate) return 1;
        if (this.calculatedDate < that.calculatedDate) return -1;
        return 0;
    }
}

    public List<Wrapper> wrappers {get; private set;}

    public CustomerStatementTableEmailController() {

        List<Delivery_Note__c> notes = [
            SELECT id,name,Balance__c,Transaction_Amount__c,Transaction_Date__c,Transaction_Number__c,Transaction_Type__c,
                Transaction_Balance__c,ABM_Invoice_Amount__c,ABM_Credit_Note_Amount__c,zTransactionBalanceForStatement__c
            FROM Delivery_Note__c
            WHERE order__r.AccountId = :recId
        ];

        wrappers = new List<Wrapper>();

        for (Delivery_Note__c note : notes) {
            wrappers.add(new Wrapper(note));
        }

        List<Remittance_Advice__c> remittances = [
            SELECT id,Name,Account__r.id,Date__c,Remittance_Total_Credit__c,Remittance_Total_Debit__c
            FROM Remittance_Advice__c
            WHERE Account__r.id = :recId
        ];

        for (Remittance_Advice__c remittance : remittances) {
            wrappers.add(new Wrapper(remittance));
        }

        wrappers.sort();

        notDue = [
            SELECT id,Invoice_Total_Amount_Including_GST__c,Balance__c,recordType.Name,Transaction_Balance__c FROM Delivery_Note__c
            WHERE zAgeTrialBalance__c = 'Current'
            AND recordType.Name = 'Delivery Note / Invoice'
            AND order__r.Accountid = :recId
            ];
        notDueC = [
            SELECT id,Credit_Note_Total_Including_GST__c,Balance__c,Transaction_Balance__c FROM Delivery_Note__c
            WHERE zAgeTrialBalanceCredit__c = 'Current'
            AND recordType.Name = 'Credit Note'
            AND order__r.Accountid = :recId
            ];
    }
}

Best Answer

The standard pattern to use in VF email component controllers is:

  • Empty constructor
  • Getters for each object that needs to be displayed in the email template - where if the getter returns a collection, be sure to return an empty collection if the passed in via assignTo attribute is null. WHY? Because when previewing the email template, the assignTo value will by default be null when you come to the preview page in the Lightning Platform Setup UI. This often appears as an ugly null pointer exception in the preview UI.

For example

public Id recId {get;set;}
public List<Delivery_Note__c> notes {
  get {
    return recid != null ? [Select ...] : new List<Delivery_Note__c>();
  } private set;
}
public Wrapper[] wrappers {
   get{
    wrappers = new List<Wrapper>(); 
    for (Delivery_Note__c note: notes) {
      wrappers.add(new Wrapper(note));
    }
    wrappers.sort();
    return wrappers;
  } private set;
 }


... repeat for other objects, all of which should be able to deal with recId == null or, if dependent on some other collection, deal with empty collections
Related Topic