Display field history for the Case object.

Rob,

This does get me significantly closer, thank you!

I’m curious if it’s easily possible to get the pretty names for these values? I’ve got references to the fields in the model, but it now looks like this:



So, the template is working great, but you can still see FLAGS__ViewedFlag__c and the case owner IDs in there.

Korey I can confirm that Custom fields are only popuplating with thier API name,  rather than the lable friendly name.  Salesforce is not giving us access to the labels in the “Fields Picklist”    About all I can reccomend is writing some Javascript that translates those values on the fly. 

Also you can put HTML tags into a template field, so you can make values bold by wrapping them in like “{{NewValue}}”.

I spent some time with this over the weekend (cuz history has been needling me for a long time) and developed a javascript solution that more completely matches the standard salesforce implementation of field level history.

Here is the end result (compared side by side with the template solution I showed above).

You can see the following:

  1. Appropriate distrinction between case created and description changed
  2. Custom fields have the API names replaced with labels.
  3. The ID values associated with Owner change have been removed.

Here is how.

  1. Add the “Field” field from your case history model to your table. Change the Field Renderer property to “Custom (run a Snippett)” and give it some name like “CaseHistory”

  1. Go to the Resources Tab and add a new Javascript resource.
  • The Type should be "In-Line (Snippet)
  • Give it the same name as you used in the field properties above.

The code in the snippet should look somthing like this. You will need to adjust to your particular situations.

var params = arguments[0], 
$ = skuid.$; 
var field = arguments[0]; 
var value1 = arguments[1]; 
var model = field.model; 
var row = field.row; 
var value2 = row.OldValue; 
var value3 = row.NewValue; 
var text; 
// Replace custom fields API name with their labels. You need to hard code label names. 
if (value1 === "Service_Location__c"){ value1 = "Service Location"; } 
// Handle case creation if (value1 == "created") 
{ text = 'Case Created'; } 
// Handle text fields where change values are not documented. 
else if (value2 === null && value3 === null) { text = 'Changed ' + value1; } 
// Handle "first entry" situations where "changed from" value is not present. 
else if (value2 === null && value3 !== null) 
{ text = 'Changed ' + value1 +' to <b>"' + value3 + '"</b>'; } 
// Handle full change record where Field, Changed From and Changed To fields are present. 
else if (value2 !== null) 
{ text = 'Changed ' + value1 + ' from "'+ value2 + '" to <b>"' + value3 + '"</b>'; 
// If changed from value looks like an ID, hide the row - since the name field is already there. 
var n = value2.indexOf("00"); 
if (n===0) 
$(document.body).one('pageload',function() { field.element.closest('tr').remove(); }); } } 
// Populate text into table cell 
field.element.append(model.mergeRow(row,text));



Rob,

This worked perfectly, and is very very close to the original Case History. Thank you so much, for taking the time to put this together. It’s greatly appreciated.

Thanks again! I owe you a beer or coffee or something!

No worries.  Having you guys as customers is prize enough. Glad I could help. 

Rob I really like what you’ve done (I didn’t know you wrote Javascript :slight_smile: ) I would just suggest using a function to handle the label text like so… instead of this

// Replace custom fields API name with their labels. You need to hard code label names.
&nbsp;if (value1 === "Service_Location__c")
{<br>&nbsp; &nbsp; value1 = "Service Location";<br>}


I would do this: (I realized after the fact that you’ll never get an __r field…)

//you can use a hack-ish function to format all instances correctly<br>
//and avoid hardcoding labels...<br>
function fixFieldFormat(field){<br>
&nbsp; &nbsp; if(field.indexOf("__c") != -1){<br>
&nbsp; &nbsp; &nbsp; &nbsp; if(field.indexOf("__r.") != -1){<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var start = field.indexOf("__r.") + 4;<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var end = field.length - start;<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log(end);<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; field = field.substr(start,end);<br>
&nbsp; &nbsp; &nbsp; &nbsp; }<br>
&nbsp; &nbsp; &nbsp; &nbsp; var trim__c = field.replace("__c","");<br>
&nbsp; &nbsp; &nbsp; &nbsp; var trim_ = trim__c.replace(/_/g," ");<br>
&nbsp; &nbsp; }<br>
&nbsp; &nbsp; return trim_;<br>
}<br>
//then just call it<br>value1 = fixFieldFormat(value1);

This would be a better version of the hack above…

//you can use a hack-ish function to format all instances correctly//and avoid hardcoding labels...<br>function fixFieldFormat(val){<br>&nbsp; &nbsp; if(val.indexOf("__c") != -1){<br>&nbsp; &nbsp; &nbsp; &nbsp; var trim__c = val.replace("__c","");<br>&nbsp; &nbsp; &nbsp; &nbsp; var trim_ = trim__c.replace(/_/g," ");<br>&nbsp; &nbsp; }<br>&nbsp; &nbsp; return trim_;<br>}<br>//then just call it<br>value1 = fixFieldFormat(value1);

Quick afterthought question: Is it possible to use this JS to hide the row instead of making a modification? So, if the value1 starts with or contains certain characters I can hide them.

I don’t really write javascript.  I’ve just watched it long enough to be able to figure out what I should copy and paste… 

Quick note about the code that hides the reference ID rows. This code will not work if the history table uses deferred rendering in any way. We had to resort to some trickery where the code runs part way at page load, but then waits for load completion before finishing the “remove rows” section. If the table is deferred (say in a tab set that deferrs rendering, or in a page include) the page load even will be complete before the table is finished rendering. We played with a few options but couldn’t come up with a better solution.

So be warned.

Rob, I was working on a similar issue tonight for Opportunity Field History.  Would your solution above work for that or does the JS need to be completely different.  Disclaimer: I really don’t know anything about JS but am an expert at Copy/Paste :slight_smile:

Thanks again Rob!

I’m hitting a new error with the JS in this script:


Uncaught TypeError: undefined is not a function

Which then points me to this line…

var n = value2.indexOf(“00”);


This only appears to happen on one case, and thus far it appears to happen because there’s a large amount of case history (or perhaps something is appearing twice). I set the table to only show 10 per page, and it now loads correctly.

Any ideas?





I ran into similar problems, when there were no entries in the Value2.   This is why I added the block immediatly above the problem line that asks "If value2 is null then…()  else use the indexOf function to find if the string starts with “00” and therefore is probably a ID record. 

So make sure that code is in place.  
Then you might add a console.log statement on the “value2” varialbe to see what values are getting returned.  We may need to add another statement to the “if null” block to detect another state that breaks the indexOf function. 


We were getting false / true, which broke indexOf, so this was added earlier in the script:

if (typeof value2 == “boolean”)
    value2 = value2.toString();
if (typeof value3 == “boolean”)
    value3 = value3.toString();

Not sure if I’ll hit anymore, but if I do I’ll make this code smarter to handle the outliers.

Thanks!

I am using this to make my template name bold and increase the font size. I really don’t know anything about HTML but got some pointers from Google. Do you see anything wrong with this approach?

Opportunity Detail

Looks like you’re missing a closing p tag…

I may have something to offer here to get the field LABEl instead of  API NAME. Say, for Case object you have a model called ‘Cases’ and for Case history object you have model called ‘CaseHistory’

Use following renderer for ‘Field’ field of Case History object.

var field = arguments[0],
    value = arguments[1],
    $ = skuid.$;
var fieldNameLabelmap = {};   
var modelFields = skuid.$M(‘Cases’).fields;

$.each(modelFields, function(i,f){
    fieldNameLabelmap[f.id] = f.label;   
});
skuid.ui.fieldRenderers.TEXT[field.mode](field, fieldNameLabelmap[value]);






I’m trying to implement this field renderer. When I set the custom renderer my page will no longer load. Here’s the javacript I’m using:

var params = arguments[0], $ = skuid.$; var field = arguments[0]; var value1 = arguments[1]; var model = field.model; var row = field.row; var value2 = row.OldValue; var value3 = row.NewValue; var text; //you can use a hack-ish function to format all instances correctly//and avoid hardcoding labels... function fixFieldFormat(val){ if(val.indexOf("__c") != -1){ var trim__c = val.replace("__c",""); var trim_ = trim__c.replace(/_/g," "); } return trim_; } //then just call it value1 = fixFieldFormat(value1); // Handle case creation if (value1 == "created") { text = 'Record Created'; } // Handle text fields where change values are not documented. else if (value2 === null &amp;&amp; value3 === null) { text = 'Changed ' + value1; } // Handle "first entry" situations where "changed from" value is not present. else if (value2 === null &amp;&amp; value3 !== null) { text = 'Changed ' + value1 +' to <b>"' + value3 + '"</b>'; } // Handle full change record where Field, Changed From and Changed To fields are present. else if (value2 !== null) { text = 'Changed ' + value1 + ' from "'+ value2 + '" to <b>"' + value3 + '"</b>'; // If changed from value looks like an ID, hide the row - since the name field is already there. var n = value2.indexOf("00"); if (n===0) { console.log('correct'); $(document.body).one('pageload',function() { field.element.closest('tr').remove(); }); } } // Populate text into table cell field.element.append(model.mergeRow(row,text));