I am using '/n' in below way

public String appComment{get;set;}
for(Processinstancehistory p:listp)
appComment=appComment +'\n'+ p.Comments+'\n';

I am getting result in one line . I need every approver comment in separate line. how can I achieve it

  • 1
    where are you displaying this concatenated comment? Dec 4, 2019 at 6:14
  • in an email template. which is having a table . The column Approval comments fetches this value
    – user75937
    Dec 4, 2019 at 6:51
  • 1
    try using <br/> Dec 4, 2019 at 7:02
  • i dont think <br/> will work inside apex class
    – user75937
    Dec 4, 2019 at 7:08
  • You cannot use apex class in any of the email templates text/custom/visualforce email templates. Unless you mean, you want to send an email via apex?
    – compski
    Dec 4, 2019 at 8:03

1 Answer 1


Disclaimer: I was bored so I wrote out what I hope is a pretty detailed code sample. This is as far as I'm willing to go however.

If you're using Visualforce for your email template per your comments, you want to do something like this:

public List<String> appComments {get; set;}

Then in the constructor:

this.appComments = new List<String>();

Then somewhere else in your code where you are currently running your loop:

for(Processinstancehistory p:listp) {

Then in your VF template

<apex:repeat value="{!appComments}" var="comment"> 
<!-- or apex:dataTable or some other iteration component -->
    <apex:outputText value="{!comment}"/>

Alternatively, if you wanted to do a table I wouldn't even bother creating a list of strings. Instead you could use your list of records:

public Id recordId {get; private set;}
public Case record {get; private set;} // just a sample, youll use what ever object you want
public Map<Id, List<ProcessInstanceHistory>> histories {get; private set;}

// Assuming you're using a custom controller
public customControllerConstructor() {
    Id recordId;

    try {
        recordId = ApexPages.CurrentPage().getParamters().get('recordId');
        // Never use Id as a custom parameter name, this is a reserved param for sfdc
    } catch (Exception ex) {
        // If the Id is not valid, you may get an error
        // handle your error here


// If you're using an extention
public customControllerConstructor(ApexPages.standardController stdCon) {
    this.recordId = stdCon.getRecordId();

public void init() {
    if (this.recordId != null) {
        this.record = [
            SELECT Id, Fields..., (
                SELECT Id, Fields...
                FROM ProcessInstances
            FROM Case
            WHERE Id = :

        histories = new Map<Id, List<ProcessInstanceHistory>>();

        for (Processinstancehistory history : [
            SELECT Id, Comments, Actor.Name
            FROM Processinstancehistory
            WHERE ProcessInstanceId IN :this.record.ProcessInstances
        ]) {
            if (!this.histories.get(history.ProcessInstanceId) == null) {
                // Contains key will tell you if the key exists but not if the list
                // is initialized so we use .get == null instead
                this.histories.put(history.ProcessInstanceId, new List<Processinstancehistory>());


Then in your VF Page:

<apex:repeat value="{!record.ProcessInstances}" var="instance">
    <apex:dataTable value="{!histories[instance.id]}" var="history">
        <apex:column value="{!history.Actor.Name}"/>
        <apex:column value="{!history.comments}"/>

You must log in to answer this question.

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