function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
dkraundkraun 

How to add Histories relatedList to a custom visualforce page

Hi,

I need to add the field history from a custom object to a page, but am running into a lot of problems.  The related list displays with no problem when viewing the custom object through the salesforce custom object tab, but on my custom page it says that Histories is not a valid child relationship name for the entity.  I have checked the child relationships of my custom object and the History object that salesforce generates automatically when "Track Field History" is checked is listed with the name "Histories" as the child relationship name.  I have tried this on multiple custom objects (making them as simple as possible) and a relatedList on Histories just doesn't seem to be possible.  Has anyone been able to make this work?

-David
billgreenhawbillgreenhaw
How are you referencing the field history object?  You should reference it like this - [custom object name]__History .  (two underscores, just like __c on custom fields and objects)

You can look at the WSDL to get this name, use Eclipse to see the schema, or use Apex Explorer.

dkraundkraun
Hi,

My object name is CRM_Tickets, and I access this through the api name CRM_Tickets__c.  I have now tried:

<apex:relatedList subject="{!ticket.Id}" list="CRM_Tickets__History"/>

but that also gives me the error of 'CRM_Tickets__History' is not a valid child relationship name for entity CRM_Ticket.

I have used the apex explorer to check on the child relationship name and I found:

Tickets__c >> Child Relationships >> Histories >> Child Relationship: Tickets__History, Relationship Name: Histories

This is why I was trying to use Histories as the list attribute in my original post.  Any ideas?
dkraundkraun
Correction, CRM_Tickets__c instead of Tickets__c and CRM_Tickets__History instead of Tickets__History in:

Tickets__c >> Child Relationships >> Histories >> Child Relationship: Tickets__History, Relationship Name: Histories
jeroenjeroen
I am Having similar problems, I tried everything, but nothing works!
 
This can't be difficult I thought.....:smileysad:
 
Anyone?
Ron HessRon Hess
Histories is a special related list. you can build it but need to build your own list, something like this

Code:
<apex:page standardcontroller="case" >
    <apex:form >
        <apex:pageBlock id="thePageBlock">
            
            <apex:pageBlockTable value="{!case.Histories}" var="h">
                <apex:column headerValue="Date"  value="{!h.createddate}"/>
                <apex:column headerValue="What" value="{!h.field}"/>
                <apex:column headerValue="From" value="{!h.oldvalue}"/>
                <apex:column headerValue="To"   value="{!h.newvalue}"/>
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

 

jeroenjeroen

That works! :smileyvery-happy:

 

Thanks

Jeroen

BDArnzBDArnz
Not for Me!  :smileysad:
 
The only change to your code I made was to go from {!Case.Histories} to {!Service__c.Histories}.  (I'm trying to show the history for a custom object called 'Service'.      
 
 
     <apex:pageBlock id="svcHistory">
           
            <apex:pageBlockTable value="{!Service__c.Histories}" var="h">
                <apex:column headerValue="Date"  value="{!h.createddate}"/>
                <apex:column headerValue="What" value="{!h.field}"/>
                <apex:column headerValue="From" value="{!h.oldvalue}"/>
                <apex:column headerValue="To"   value="{!h.newvalue}"/>
            </apex:pageBlockTable>
        </apex:pageBlock>
 
I get the error:
 
Changed Field: bad value for restricted picklist field: created
 
Any idea why? 
B2000B2000

I too had the same error message for "Field".  Using Ron's pageBlock technique and using the WSDL, I want the "Field" element to be displayed. The createddate and CreatedById by themselves display correctly in the VF page.  The "Field" element causes the error messages.

 <element name="Field" nillable="true" minOccurs="0" type="xsd:string" /> 

Code:
<apex:pageBlockTable value="{!SCRB_SalesOrder__c.Histories}" var="h">
      <apex:column headerValue="Date" value="{!h.createddate}"/>
      <apex:column headerValue="User" value="{!h.CreatedById}"/>
      <apex:column headerValue="Action" value="{!h.Field}"/>
</apex:pageBlockTable>


 

The normal View page displays the following after I created the record then made a change to the "Status Code" field.

DateUserAction
9/18/2008 7:04 PM    xxxxxxxxxxx   Changed Status Code from Draft to Pre-build.
9/13/2008 2:22 PM    xxxxxxxxxxx  

   Created.

The VF page generated the following error messages:
After the record was created:
Changed Field: bad value for restricted picklist field: created
 
After one field was changed:
Changed Field: bad value for restricted picklist field: StatusCode__c


Message Edited by Brian Conlon on 09-18-2008 07:40 PM
hemmhemm
I was having issues putting markup only into the VF page.  There were rare scenarios where I had a date field being tracked and I'd get string to date errors.  See this post for more info on that error.

I got a nice Field History list by adding a ControllerExtension onto my VF page.  I used a wrapper class to return the same 3 fields (date, user and action) that appear in other history lists.  I did a bit of fanciness to provide a different action for a few different scenarios. 

It's important that the VF page outputtext attribute for the action property has "escape=false".  This will allow the bold html to render.

My VF markup says...
Code:
<apex:pageBlock title="Fulfillment History">
    <apex:pageBlockTable value="{!histories}" var="h">
        <apex:column headerValue="Date"  value="{!h.thedate}"/>
        <apex:column headerValue="User" value="{!h.who}"/> 
        <apex:column headerValue="Action"><apex:outputText escape="false" value="{!h.action}"/></apex:column>
    </apex:pageBlockTable>
</apex:pageBlock>

 
My Controller is...
Code:
public class Fulfillment_ControllerExtension {

 private final Fulfillment__c f;
 
    public Fulfillment_ControllerExtension(ApexPages.StandardController stdController) {
     f = [select id, createddate, createdby.name from Fulfillment__c where id =:ApexPages.currentPage().getParameters().get('id')];
    }
    
    public list<cHistories> getHistories() {
     
     
     // Initialize list to be returned
     list<cHistories> list_ch = new list<cHistories>();
     
     
     
     // Loop through all field history records
     for (Fulfillment__History fh: [select 
          ParentId, 
          OldValue, 
          NewValue, 
          IsDeleted, 
          Id, 
          Field, 
          CreatedDate, 
          CreatedById,
          CreatedBy.Name
          From Fulfillment__History
          where ParentId = :f.id
          order by CreatedDate desc
          ]) {
   
   // Create a new wrapper object
   cHistories ch = new cHistories();
       
       // Set the Date
       ch.theDate = String.valueOf(fh.createddate);
       
       // Set who performed the action
       ch.who = fh.createdby.name;
       
       // Set the Action value
       if (String.valueOf(fh.Field) == 'created') {    // on Creation
        
        ch.action = 'Created.';
        
       } else if (fh.OldValue != null && fh.NewValue == null){ // when deleting a value from a field
        
        // Format the Date and if there's an error, catch it and re
        try {
         ch.action = 'Deleted ' + Date.valueOf(fh.OldValue).format() + ' in <b>' + String.valueOf(fh.Field) + '</b>.';
        } catch (Exception e){
         ch.action = 'Deleted ' + String.valueOf(fh.OldValue) + ' in <b>' + String.valueOf(fh.Field) + '</b>.';
        }
        
       } else {             // all other scenarios
        
        String fromText = '';
        if (fh.OldValue != null) {
         try {
          fromText = ' from ' + Date.valueOf(fh.OldValue).format();
         } catch (Exception e) {
          fromText = ' from ' + String.valueOf(fh.OldValue);
         }
        }
        
        String toText = '';
        try {
         toText = ' from ' + Date.valueOf(fh.NewValue).format();
        } catch (Exception e) {
         toText = ' from ' + String.valueOf(fh.NewValue);
        }
        
        ch.action = 'Changed <b>' + String.valueOf(fh.Field) + '</b>' + fromText + ' to <b>' + toText + '</b>.';
        
       }
      list_ch.add(ch);    
           
  }
  
  
  
  return list_ch;
    }
    
    public class cHistories {
     
     // Class properties
        public String theDate {get; set;}
        public String who {get; set;}
        public String action {get; set;}
        
        
    }

}

 

CTU007CTU007

I figured out a simple way to add field history as a tab, not optimum as you need to click the link to view it, but NO need for custom controller, or extension:

 

 

<apex:tab label="Field History" name="Field History" id="fieldhistory" rendered="{!$Profile.Id!='00e0xxxxxxxx" >
         <apex:outputLink value="https://ssl.salesforce.com/_ui/common/history/ui/EntityHistoryFilterPage?id={!opportunity.id}">
            click to view field history
         </apex:outputLink> 
        
      </apex:tab>

 

 

rcravenrcraven
Why is the h.field restricted?  So basically, your solution doesn't work, thanks!  We need to recreate the related list from scratch....great feature of the apex related list tag, good thing it's documented as well.
rcravenrcraven
Does anyone know how to add a custom related list, so that it shows up as a hover link at the top of the page?
smackafeesmackafee

Interesting.  I found this solution, and applied it to CaseComments.  It worked, but only when I removed all but a couple fields:

 

 

    <apex:form >
        <apex:pageBlock id="CommentsBlock" title="Case Comments / Notes">
            
            <apex:pageBlockTable value="{!case.CaseComments}" var="cc">
                <apex:column headerValue="Date" value="{!cc.createddate}"/>
                <apex:column headerValue="Comment" value="{!cc.CommentBody}"/>
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>

 

Not sure what the specifics are about this, but I have my desired result.

 

EvaDEvaD

I used Hem's solution and it works great!  I am so glad you posted the code.  I made a few changes for our Org but sure saved me a lot of time!

 

Thanks

EvaD

PaulyPauly

It would seem that the Changed Field has some some special validation that occurs when used directly as the value of an <apex:column>. I was able to get this markup to work with my Trade__c custom object:

 

 

<apex:pageBlock id="history">
    <apex:pageBlockTable value="{!Trade__c.Histories}" var="h">
        <apex:column headerValue="Date" value="{!h.CreatedDate}"/>
        <apex:column >
        	<apex:facet name="header">Change</apex:facet>
        	<apex:outputText value="{!h.Field}" />
        </apex:column>
        <apex:column headerValue="From" value="{!h.OldValue}"/>
        <apex:column headerValue="To"   value="{!h.NewValue}"/>
        <apex:column headerValue="By"   value="{!h.CreatedBy.Name}"/>
    </apex:pageBlockTable>
</apex:pageBlock>

 Cheers,

-Pauly

 

SNegiSNegi

Thanks hemm & Pauly ! your solutions worked for me !

 

I modified hemm's code to take approval Process fields values (locked/unlocked), now sharing it to others reference.

_____________________________________________________________________________ 

 

//___________________________ VF Code ___________________________________

    <apex:pageBlockTable value="{!histories}" var="h">
            <apex:column headerValue="Date"  value="{!h.thedate}"/>
            <apex:column headerValue="User" value="{!h.who}"/>
            <apex:column headerValue="Action"><apex:outputText escape="false" value="{!h.action}"/></apex:column>
`    </apex:pageBlockTable>
   
    //____________________________________________ APEX CODE ___________________
    // Wrapper class to show history in VF
    public class cHistories {    
     // Class properties
        public String theDate {get; set;}
        public String who {get; set;}
        public String action {get; set;} 
    }
   
    public list<cHistories> getHistories() {    
    
     // Initialize list to be returned
     list<cHistories> list_ch = new list<cHistories>();
     // Loop through all field history records
     for (LN_PST_Library__History fh: [select ParentId, OldValue, NewValue, IsDeleted, Id, Field, CreatedDate, CreatedById, CreatedBy.Name From LN_PST_Library__History where ParentId = :tempPstObj.id order by CreatedDate desc]) {  
        // Create a new wrapper object
        cHistories ch = new cHistories();      
       // Set the Date
       ch.theDate = String.valueOf(fh.createddate);      
       // Set who performed the action
       ch.who = fh.createdby.name;      
       // Set the Action value
       if (String.valueOf(fh.Field) == 'created') {    // on Creation       
            ch.action = 'Created.';       
       } else if (fh.OldValue != null && fh.NewValue == null){ // when deleting a value from a field       
            // Format the Date and if there's an error, catch it and re
            try {
                 ch.action = 'Deleted ' + Date.valueOf(fh.OldValue).format() + ' in <b>' + String.valueOf(fh.Field) + '</b>.';
            } catch (Exception e){
                 ch.action = 'Deleted ' + String.valueOf(fh.OldValue) + ' in <b>' + String.valueOf(fh.Field) + '</b>.';
            }       
       } else { // all other scenarios       
       if(String.valueOf(fh.Field) == 'locked'){
            ch.action = 'Record <b>' + String.valueOf(fh.Field) + '</b>.';       
       }
       else if(String.valueOf(fh.Field) == 'unlocked'){
            ch.action = 'Record <b>' + String.valueOf(fh.Field) + '</b>.';       
       }
       else{
          String fromText = '';
            if (fh.OldValue != null) {
             try {
                  fromText = ' from ' + Date.valueOf(fh.OldValue).format();
             }catch (Exception e) {
                fromText = ' from ' + String.valueOf(fh.OldValue);
             }
        }       
        String toText = '';
        try {
            toText = '' + Date.valueOf(fh.NewValue).format();
        } catch (Exception e) {
            toText = '' + String.valueOf(fh.NewValue);
        }       
        ch.action = 'Changed <b>' + String.valueOf(fh.Field) + '</b>' + fromText + ' to <b>' + toText + '</b>.';       
       }
       }
      list_ch.add(ch);              
    }
    return list_ch;
   }

_____________________________________________________________________________

 

Njoy & Happy Coding !

~ Sushil

kavya.ax1320kavya.ax1320

Can you please tell me how to write test class for the above code.