0

I'm really confused by order of execution. I have tried with a few posts, but mine does not work. Please help with taking a look.

I am able to receive the templated email, but there's no incident fields contents rendered in the email. The received email shows the No incidents to report. If I set Test with merge fields, and provide static environment Id and contactId, then it works and show the binded incidents in the received email. The environmentId seems not passed into the template/controller.

The ultimate goal is to trigger email firing by scheduled apex job. In testing phase, I'm using following code to run, and can receive fired email. But the email content is No incidents to report.

SenderForIncidents sender = new SenderForIncidents();
sender.execute(null);
public with sharing class SenderForIncidents Implements Schedulable{
    public void execute(SchedulableContext sc){
        // Queries prepared template, orgWideEmail, and contactList
        EmailTemplate template = [SELECT Id FROM EmailTemplate WHERE... LIMIT 1];
        OrgWideEmailAddress orgWideEmail = [SELECT Id FROM OrgWideEmailAddress WHERE ... LIMIT 1];
        List<Contact> contactList = [SELECT ......];

        List<Messaging.SingleEmailMessage> emailSendList = new List<Messaging.SingleEmailMessage>();
        for (Contact c : contactList){
            Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
            email.setOrgWideEmailAddressId(orgWideEmail.Id);
            email.setTemplateId(template.Id);
            email.setTargetObjectId(c.Id);
            email.saveAsActivity = false;
            email.setWhatId(relatedToTypeObjectId); UPDATE-------> SOLUTION!!!!!!
            emailSendList.add(email);
        }
        Messaging.sendEmail(emailSendList, false);
    }
}

============ Below are email template, VF component, and controller info.===============

Email template

<messaging:emailTemplate subject="{!relatedTo.Name}" 
                         recipientType="Contact" 
                         relatedToType="Environment__c">
     <c:MyController environmentId="{!relatedTo.Id}" />
</messaging:emailTemplate>

Visual force component

<apex:component controller="MyController" access="global">
    <apex:attribute name="environmentId" type="Id" 
                   description="Environment Id" assignTo="{!envId}" />
    <apex:outputPanel rendered="{!!incidents.empty}">
        <apex:repeat value="{!incidents}" var="incident">
             <p>{!incident.fieldA__c}</p>
    </apex:outputPanel>
    <apex:outputPanel rendered="{!incidents.empty}">
        <p> No Incidents to Report. </p>
    </apex:outputPanel>
</apex:component>

controller

Suggested controller version from first answer.
public class MyController() {
    public Id envId;
    public void setEnvId(Id envIdValue) {
        List<Object2> matchedObj2 = [SELECT Case__c FROM Object2 where Environment__c = :envId];
            List<Id> caseIds = new List<Id>();
            for (Object2 obj : matchedObj2) {
                caseIds.add(obj.Case__c);
            }

            incidents = [SELECT ... FROM Case WHERE Id in :caseIds ];
    }

    public MyController(){}
    public List<Case> incidents {get; set;}   
}
My original version
/*public class MyController {
    public Id envId {get; set;}

    public MyController(){}

    public List<Case> incidents;
    public List<Case> getIncidents() {
        if (incidents == null) {
            List<Object2> matchedObj2 = [SELECT Case__c FROM Object2 where Environment__c = :envId];

            List<Id> caseIds = new List<Id>();
            for (Object2 obj : matchedObj2) {
                caseIds.add(obj.Case__c);
            }
            incidents = [SELECT .... FROM Case WHERE Id in :caseIds AND ...];
        }

        return incidents;
    }
}*/

I have tried to test with merge field in email template with EnvironmentId and contact, it works. Seems either the template is not associating to environment__c, or the value was not passed to the controller. Generous help appreciated. Thank you.

7
  • please add the VF markup for the custom component as your first version is very close
    – cropredy
    Nov 16, 2019 at 6:24
  • @cropredy Hi, thanks for your reply. Can you please explain how I should try it? I do not get it. Thank you very very much. Nov 18, 2019 at 21:27
  • please add the VF markup for the custom component using edit
    – cropredy
    Nov 18, 2019 at 21:29
  • @cropredy please check the updated VF component code. Appreciate for your quick reponse. Nov 18, 2019 at 21:31
  • @cropredy Hi, thank you so much for your quick response and answer. I have replaced my controller to the answer you suggested. Sadly, no incidents contents were rendered in the sent out email. I tried to Send test and verify merge fields with filled static envId and contact, there are incidents contents sent out. How can we testify that Environment__c is binded appropriately please? Or do you spot anything else I did not correct? Appreciate so much of your kindness and wisdom. Nov 18, 2019 at 23:25

2 Answers 2

0
public class MyController {
    public Id envId {get; set;}

    public MyController(){}

    public List<Case> incidents {
        get {
           if (incidents == null) {

             incidents = new List<Case> ();
             if (this.envId != null) {
               Object2[] matchedObj2 = [SELECT Case__c FROM Object2 where Environment__c = :this.envId];

               List<Id> caseIds = new List<Id>();
               for (Object2 obj : matchedObj2) {
                 caseIds.add(obj.Case__c);
               }
               incidents = [SELECT .... FROM Case WHERE Id in :caseIds AND ...];
             }
           }

           return incidents;
       } private set;
  }

This should work (albeit untested). I've never used the .empty for VF collection size testing although I see others have shown this to work. I prefer a getter method hasIncidents in a controller that tests for empty collection or can be extended for special cases.

Update

You posted the schedulable class's execute and the relevant section is here:

Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
        email.setOrgWideEmailAddressId(orgWideEmail.Id);
        email.setTemplateId(template.Id);
        email.setTargetObjectId(c.Id);
        email.saveAsActivity = false;
        emailSendList.add(email);

Since your email template has a relatedToType="Environment__c", you need to tell from Apex an ID pointing to a single Environment__c. This is done with the setWhatId(..) method

6
  • if send test and verify merge fields works; then how are you invoking the email template when it fails? Perhaps therein lies your issue. Please don't respond in comments. Use edit on the OP to elucidate
    – cropredy
    Nov 19, 2019 at 0:03
  • Hi, thank you for the follow-up. I should have put more VF info in the question. Added 'No Incidents to Report'. When test with merge fields and provide static envId and contactId, the received email has incident contents. But when running by class call, the received email says 'No incidents to report'. Thank you. Nov 19, 2019 at 17:14
  • Thank you for that great call. I thought envId{get;set;} will magically link for me... lol... Now that, for each contact person, there can be multiple environments associated with the contact person. However, the requirement is, contact wants to receive 1 single email for EACH associated environment. I have no idea of how to implement the setEnvId to fulfill the 2 requirements. 1: make sure only 1 env for each email. 2: All environments associated with this contact are looped through. Should I create another ticket for this? Greatly appreciate. Nov 19, 2019 at 18:56
  • You should accept the answer and then think about how you might accomplish your requirement, working on it, then, if stuck, posting a new question with the relevant code from the schedulable class and the email template (as the email template controller or custom controller are no longer an issue). I think you have enough knowledge by now to succeed!
    – cropredy
    Nov 19, 2019 at 20:20
  • Hi, I got stuck for 4 days on salesforce.stackexchange.com/questions/285893/…. did some research but still don't get if setWhatId can be of use to my current problem. Any clues please? Nov 25, 2019 at 2:29
0

Your problem is using a getter to load the data, plus a lazy load pattern. Because getters can be called several times in a transaction, you should avoid this design.

Instead, use a setter:

public void setEnvId(Id value) {
  loadIncidenceList(value);
}
public case[] incidents { get; set; }
void loadIncidenceList(Id envId) {
  // your original logic here //
}
1
  • Hi, thank you very much for replying. please see the updated controller. Sadly, no data rendered in email yet. Nov 15, 2019 at 22:30

You must log in to answer this question.

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