1

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
            ];
    }
}

2 Answers 2

1

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
2
  • Thanks cropredy!, I guess why this has been hard to understand is that although the notes list is outside of the wrapper, I thought the wrapper was called first (like the constructor) I will try and incorporate your suggestion! Commented Jan 16, 2019 at 2:11
  • @TheMikeyBoosh - the wrapper will be called first but as it references the notes list , the getter on notes gets called. Everything is returned on demand but only when needed
    – cropredy
    Commented Jan 16, 2019 at 4:18
0

As constructor loads first on page load whereas get;set; property loads when it get caught on page. So, you can not get it in constructor theoretically.

So, instead of preparing result, use getter method which is placed in component after {!recId} to make sure that it would get load after it and prints the response:

public class CustomerStatementTableEmailController {
    public Id recId {get;set;}
    public class WrapperClass{
        public List <Delivery_Note__c> notDue {get;set;}  //less than 30
        public List <Delivery_Note__c> notDueC {get;set;}
    }

    public WrapperClass getDueC(){
        //place all your code here
        //use wrappepr to return data to vf
    }
}

In VF Compoenent:

<apex:component access="global" controller="CustomerStatementTableEmailController">
  <apex:attribute name="recId" type="String" description="Attribute for Task id" required="required" assignTo="{!tskId}" />

  {!DueC} 
</apex:component>
6
  • Thanks Ayub, however I don't follow? On other posts, such as this [one] (salesforce.stackexchange.com/questions/35268/…) mention an alternative method? I just cant make sense of what you are suggesting? Commented Jan 9, 2019 at 6:58
  • Suggestion is to use getter method instead of constructor: developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/… ...in given link (provided by you) suggested approach is same using getter properties ...
    – Ayub
    Commented Jan 9, 2019 at 7:07
  • So using your example, what is getDueC? And when i write how you have explained i get a "Variable does not exist: notDue" error. I think where i am confused is where you have written "place all your data here". Commented Jan 9, 2019 at 7:19
  • I just showed u an example....get keyword makes it usable on vf page as getter methods that would return result in vf compoent direct....give a practical try..u will understand that..
    – Ayub
    Commented Jan 9, 2019 at 7:45
  • Hi Ayub, I understand what you are saying (sort of) however I cant apply it to my copied code above. Commented Jan 15, 2019 at 23:34

You must log in to answer this question.

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