I've done something similar in the past, but it requires that you manually do the template merge field lifting. Here is an example of how you could do it below.
Since you are manually doing the merging in the template, you can put any kind of merge field into your template that you'd like. In this example, I'm pretending there is a custom merge field {!myCustomString} in the email template for your custom string you want to insert.
I've also assumed below that you have a merge field for first name {!Contact.FirstName} in the subject and in the body of the template. If you have other fields, you would need to include the fields in the contact query and then to the string replace for each of those fields.
public static void sendSingleMail(Id contactId, Id templateId, String fromAddress, String myCustomString){
// grab the email template
EmailTemplate emailTemplate = [select Id, Subject, HtmlValue, Body from EmailTemplate where Id =: teamplateId];
// grab the contact fields we need. This assumes we are emailing a contact.
Contact c = [Select Id, FirstName FROM Contact WHERE Id=: contactId];
// process the merge fields
String subject = emailTemplate.Subject;
subject = subject.replace('{!Contact.FirstName}', c.FirstName);
String htmlBody = emailTemplate.HtmlValue;
htmlBody = htmlBody.replace('{!Contact.FirstName}', c.FirstName);
htmlBody = htmlBody.replace('{!myCustomString}', myCustomString);
String plainBody = emailTemplate.Body;
plainBody = plainBody.replace('{!Contact.FirstName}', c.FirstName);
plainBody = plainBody.replace('{!myCustomString}', myCustomString);
//build the email message
Messaging.Singleemailmessage email = new Messaging.Singleemailmessage();
email.setReplyTo(fromaddress);
email.setSenderDisplayName(fromaddress);
email.setTargetObjectId(objId);
email.setSaveAsActivity(true);
email.setSubject(subject);
email.setHtmlBody(htmlBody);
email.setPlainTextBody(plainBody);
Messaging.sendEmail(new Messaging.SingleEmailmessage[] {email});
}
OK, I apologize for the comment thread but this appears to be the essence of your issue
You have (and I have annotated)
// Fetch EmnailTemplate as it was last defined in the Point-and-click editor
EmailTemplate emailTempExec = [select Id, Name, Subject, Markup, HtmlValue, Body from EmailTemplate where Name = 'OpptyCertExecSummary'];
String subject, body;
// Modify the subject
subject = emailTempExec.Subject + ' ' +startDate + ' - ' + endDate;
// Modify the Body
body = 'Opportunity Certification Executive Summary '+startDate + ' - ' + endDate+'<br/>' + '<br/>' +
emailTempExec.HtmlValue;
List<Messaging.SingleEmailMessage> reminderMails = new List<Messaging.SingleEmailMessage>();
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
// Use the modified subject and body in the outbound email message
mail.setSubject(subject);
mail.setHtmlBody(body);
...
reminderMails.add(mail);
if(reminderMails.size()>0 && !Test.isRunningTest())
Messaging.sendEmail(reminderMails,false); // send the email
Instead, let SFDC do its work populating the email template at run time. Your email template is (mostly) a custom component with controller. But this will never execute because you are never asking Apex outbound email to assemble the email using the template - so the template is never rendered with its VF controller constructor or methods ever called.
A more common pattern would be:
// Fetch EmailTemplate as it was last defined in the Point-and-click editor
EmailTemplate emailTempExec = [select Id from EmailTemplate where Name = 'OpptyCertExecSummary'];
List<Messaging.SingleEmailMessage> reminderMails = new List<Messaging.SingleEmailMessage>();
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
// tell SFDC to use your template
mail.setTemplateId(emailTemplateExec.Id);
...
reminderMails.add(mail);
if(reminderMails.size()>0 && !Test.isRunningTest())
Messaging.sendEmail(reminderMails,false); // send the email
If you need a dynamic subject, you can use mail.setSubject(...);
The body portion you are synthesizing:
Opportunity Certification Executive Summary '+startDate + ' - ' + endDate
can be part of the template itself along with merge fields that resolve to controller properties.
Best Answer
There are few things wrong,
Instead of such complex code, you can do it
So your code will be like
SRC: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_email_outbound_single.htm
SRC:https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_email_outbound_messaging.htm