Display field history for the Case object.

  • 1
  • Question
  • Updated 2 years ago
  • Answered
I am going to start with a broad question, and then add as much detail as I can for this one.

I have this related list in Salesforce:



I want to re-create this in Skuid, and I have an obvious issue:

The pretty "Action" part of the list is coming from another plane of existence that does not appear on the CaseHistory object.

I've created a table based on CaseHistory, and I am able to display it like so:




However, the Changed Field does not get a "pretty name" and the Old Value / New Value fields are doubling up, so if a user 'Korey' changes the case ownership, it will show both 'Korey' as well as the Id which is less than helpful.

I can possibly fix this a number of ways:

  • Include the entire related list via a VF page in an iframe.
or
  • Pretty up the Changed Field by finding what it is supposed to be.
  • Make a text field for New Value, so I can actually filter it based on "does not start with" so I only see non Id specific entries (might not be possible, since I can't change CaseHistory) and New Value is an anytype and not a string.

To summarize, I am basically not sure if I am approaching this correctly. I am not sure where "Action" materializes itself in SOQL (if it even does), and dumping an iframe in, is messy at best.
Photo of Korey

Korey

  • 1,108 Points 1k badge 2x thumb

Posted 4 years ago

  • 1
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
Korey,  the Salesforce history display is a murky place and Skuid is only going to be partially successful in reproducing the standard display. 

The Action column in the standard display is not an actual field,  but rather a concatenation of the Field, Changed From and Changed to values.  There are also some speical situations like case creation where new and old values are not populated.  A pretty accurate representation of this is possible in Skuid using a template field that does this concatenation and uses the conditional capabilities of the Skuid Template Syntax to modify the display in the special situations. 

Use the syntax below to create a somewhat acceptable action display.  Put this in a Template on the Case History Table. 

{{#NewValue}}{{Field}} changed{{#OldValue}} from "{{OldValue}}"{{/OldValue}} to "{{NewValue}}"{{/NewValue}}{{^NewValue}}{{Field}}{{/NewValue}}

Look at "sections" in  this help file   for more information about conditional use of the Skuid Template Syntax

Just for some more reference here is what my table looks like in the buider: 



Changed By is the Created By field,  Date is the CreatedDate field with custom labels. 

Here is what the product looks like rendered. 



Now a few notes. 

1. Some fields do not populate the old or new value fields.  I think Long Text fields are excluded.  In this case just the field name that was changed is put in the Field value.  However, that value also has "created,  closed,  esclalated" etc.  And so it is hard to parse when to say "Changed description" and when to say "Case created"  Some Javascript could be used with a series of case statements,  but I'm trying a simple solution. 

2. Reference fields are problematic.  Salesforce uses a special field type called "AnyType" and then populates rows in history for both the ID field and the Name field of the reference.  Skuid cannot run conditions very effectively on the "AnyType" field and so we cannot exclude the rows that show the numeric ID's.  This is why you will see a two change rows above for record type - one being the name, and the second being the record type Id number.   Again maybe some javascript could be used to exlcude those rows client side... 

Maybe that gets you close enough? 
Photo of Korey

Korey

  • 1,108 Points 1k badge 2x thumb
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.
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
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. 
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Also you can put HTML tags into a template field, so you can make values bold by wrapping them in <b></b> like <b>"{{NewValue}}"</b>.
Photo of Bridget Dubas

Bridget Dubas

  • 212 Points 100 badge 2x thumb
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?
<p style="font-size:15px"><b>Opportunity Detail</b>
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Looks like you're missing a closing p tag...
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
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"




2. 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) {
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));
Photo of Bridget Dubas

Bridget Dubas

  • 212 Points 100 badge 2x thumb
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 :)
Photo of Korey

Korey

  • 1,108 Points 1k badge 2x thumb


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!
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
No worries.  Having you guys as customers is prize enough. Glad I could help. 
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
Rob I really like what you've done (I didn't know you wrote Javascript :) ) 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. if (value1 === "Service_Location__c"){
    value1 = "Service Location";
}

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
//and avoid hardcoding labels...
function fixFieldFormat(field){
    if(field.indexOf("__c") != -1){
        if(field.indexOf("__r.") != -1){
            var start = field.indexOf("__r.") + 4;
            var end = field.length - start;
            console.log(end);
            field = field.substr(start,end);
        }
        var trim__c = field.replace("__c","");
        var trim_ = trim__c.replace(/_/g," ");
    }
    return trim_;
}
//then just call it
value1 = fixFieldFormat(value1);
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
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... 
Photo of Moshe Karmel

Moshe Karmel, Champion

  • 8,646 Points 5k badge 2x thumb
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...
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);
Photo of Korey

Korey

  • 1,108 Points 1k badge 2x thumb
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.
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
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.

 
Photo of Korey

Korey

  • 1,108 Points 1k badge 2x thumb
Thanks again Rob!
Photo of Korey

Korey

  • 1,108 Points 1k badge 2x thumb
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?
Photo of Rob Hatch

Rob Hatch, Official Rep

  • 44,006 Points 20k badge 2x thumb
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. 
Photo of Korey

Korey

  • 1,108 Points 1k badge 2x thumb
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!
Photo of Gyan

Gyan

  • 1,400 Points 1k badge 2x thumb
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]);
Photo of Brayden Smith

Brayden Smith

  • 1,078 Points 1k badge 2x thumb
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 && 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) {

        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));