Always use a custom renderer for a specific field

  • 1
  • Question
  • Updated 3 years ago
  • Answered
Is there a way to write some kind of global script that would force a field to use a specific custom renderer for every instance of that field? I have a few fields that I need to use a custom renderer for EVERY TIME I use the field, and it would be convenient to not have to remember to change the rendering parameter of the field each time I add it in the builder.

Any ideas?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb

Posted 3 years ago

  • 1
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Ok. Very nice.

But what if I don't want to override the field renderer for every PHONE field, I just want to override for every instance of the specific field 'Primary_Phone__c'?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Ok, I couldn't wait. Anyone able to check my work on this? Does this even make sense?

(function(skuid){

// Get references to our original field renderers
var fieldRenderers = skuid.ui.fieldRenderers; 
var picklistRenderers = fieldRenderers.PICKLIST;
// My custom Picklist field renderer
var checkPicklistRenderer = function(field, value) {
//Object as switch statement
var globalPickFields = {
'Case_Type__c': 'renderCaseTypes',
'Primary_Phone_Type__c': 'renderPhoneTypes',
'Alternate_Phone_Type': 'renderPhoneTypes',
'Interaction_Intention__c': 'renderPregnancyIntentions',
'Pregnancy_Intention__c': 'renderPregnancyIntentions',
'Pregnancy_Intention_on_Intake__c': 'renderPregnancyIntentions',
'Role__c': 'renderStaffRoles',
'Primary_Staff_Role__c': 'renderStaffRoles',
'Additional_Staff_Role__c': 'renderStaffRoles',
'STD__c': 'renderSTDs',
'Referral_Source__c': 'renderReferralSources',
'Results__c': 'renderPregnancyTestResults',
};
if (globalPickFields[field.id]){
//Use a custom renderer
skuid.snippet.getSnippet(globalPickFields[field.id])(field,value);
} else {
//Use the standard renderer
picklistRenderers[field.mode](field,value);
}
 
};
   // Override all of Skuid's standard Picklist field renderers to call our custom renderer
   picklistRenderers.read = picklistRenderers.readonly = picklistRenderers.edit = function(){
       checkPicklistRenderer.apply(this,arguments);
   };

})(skuid);
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
I feel like I'm in danger of a loop here. Is there any way to still use the standard renderer if the check fails? As it's currently written, if field.id isn't a key in the globalPickFields object, I'm going to keep checking infinitely, right?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Hey, friends. This isn't answered!

Can anyone help me avoid the infinite loop?
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
And I've confirmed that it is an infinite loop.
Photo of J.

J., Official Rep

  • 7,470 Points 5k badge 2x thumb
Matt,

I think the bit that you are missing from Zach's response is the originalPhone part. Your script is completely replacing the renderers that Skuid uses, including the standard renderers that you still want to use if the field isn't one of your globalPickFields. Try something like this:
(function(skuid){

// Get references to our original field renderers
var picklistRenderers = skuid.ui.fieldRenderers.PICKLIST,
originalPicklistRenderers = skuid.$.extend({},picklistRenderers),

// Object as switch statement
globalPickFields = {
'Case_Type__c': 'renderCaseTypes',
'Primary_Phone_Type__c': 'renderPhoneTypes',
'Alternate_Phone_Type': 'renderPhoneTypes',
'Interaction_Intention__c': 'renderPregnancyIntentions',
'Pregnancy_Intention__c': 'renderPregnancyIntentions',
'Pregnancy_Intention_on_Intake__c': 'renderPregnancyIntentions',
'Role__c': 'renderStaffRoles',
'Primary_Staff_Role__c': 'renderStaffRoles',
'Additional_Staff_Role__c': 'renderStaffRoles',
'STD__c': 'renderSTDs',
'Referral_Source__c': 'renderReferralSources',
'Results__c': 'renderPregnancyTestResults'
};

// Override all of Skuid's standard Picklist field renderers to call our custom renderer
picklistRenderers.read = picklistRenderers.readonly = picklistRenderers.edit = function(field,value){
if (globalPickFields[field.id]){
//Use a custom renderer
skuid.snippet.getSnippet(globalPickFields[field.id])(field,value);
} else {
//Use the standard renderer
originalPicklistRenderers[field.mode](field,value);
}
};

})(skuid);
(Edited)
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Thanks, J.

What's up with the 
var orginalPicklistRenderers = $.extend({},picklistRenderers);
?

How is that different than just
var orginalPicklistRenderers = picklistRenderers;
 ?

Thanks!
Photo of J.

J., Official Rep

  • 7,470 Points 5k badge 2x thumb
jQuery.extend is a jQuery method that merges the contents of objects and returns the combined object. In this case, it's an easy way to do a shallow copy of an object. The way you were doing it was just creating a new reference to the same object, not creating a distinct copy. You could also have written the originalPicklistRenderers part as...
var originalPicklistRenderers = {
read : picklistRenderers.read,
readonly : picklistRenderers.readonly,
edit : picklistRenderers.edit,
};
That would be more explicit than using extend (which can sometimes overwrite more than you want).
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Makes sense! Thanks. :)
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
So, I'm still getting an infinite loop with the following code:

// Get references to our original picklist field renderers var picklistRenderers = skuid.ui.fieldRenderers.PICKLIST; 
var originalPicklistRenderers = {
read : picklistRenderers.read,
readonly : picklistRenderers.readonly,
edit : picklistRenderers.edit
};
//Object as switch statement - stores field API names and corresponding snippet names
var globalPickFields = {
'Case_Type__c': 'renderCaseTypes',
'Primary_Phone_Type__c': 'renderPhoneTypes',
'Alternate_Phone_Type__c': 'renderPhoneTypes',
'Interaction_Intention__c': 'renderPregnancyIntentions',
'Pregnancy_Intention__c': 'renderPregnancyIntentions',
'Pregnancy_Intention_on_Intake__c': 'renderPregnancyIntentions',
'Father_of_the_Baby_Intention__c': 'renderPregnancyIntentions',
'Role__c': 'renderStaffRoles',
'Primary_Staff_Role__c': 'renderStaffRoles',
'Additional_Staff_Role__c': 'renderStaffRoles',
'STD__c': 'renderSTDs',
'Sexual_Exposure_to_What_STD__c' : 'renderSTDs',
'STD_Picklist': 'renderSTDs',
'Referral_Source__c': 'renderReferralSources',
'Results__c': 'renderPregnancyTestResults',
};
// Check all picklists to see if they should be overridden
var checkPicklistRenderer = function(field, value) {
// If the field is one of the global picklist fields, run the snippet for it
if (globalPickFields[field.id]){
//Use a custom renderer
skuid.snippet.getSnippet(globalPickFields[field.id])(field,value);
} else {
//Use the standard renderer
originalPicklistRenderers[field.mode](field,value);
  }
};
   // Override all of Skuid's standard Picklist field renderers to call our custom renderer
  picklistRenderers.read = picklistRenderers.readonly = picklistRenderers.edit = function(){
       checkPicklistRenderer.apply(this,arguments);
   };

When I call originalPicklistRenderers[field.mode](field,value); (from this section of code or from one of the custom snippets) it's looping back to checkPicklistRenderer.apply(this,arguments);

Using the $.extend() syntax didn't help. 

Help?
Photo of J.

J., Official Rep

  • 7,470 Points 5k badge 2x thumb
Did you try it using the extend approach too? The issue could be in one of the snippets your globalPickFields references too.
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
I tried the extend approach, with the same result.

All of my custom renderers use the line
originalPicklistRenderers[field.mode](field,value);
to render, and this loops back to 
picklistRenderers.read = picklistRenderers.readonly = picklistRenderers.edit = function(field,value){
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
From the call stack, it looks like m.PICKLIST.readonly (the standard renderer) is calling f.readonly (my override). When my override tries to call my reference to the standard renderer, the loop starts again.

Somehow, my reference to the original renderer is still being overridden??
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Finally! I took a hint from Zach's original post. This seems to be working:

//////////////////////////////////////////////
// Global Renderer Override //
//////////////////////////////////////////////
// Get references to our original picklist field renderers
var picklistRenderers = skuid.ui.fieldRenderers.PICKLIST; 
var oPickRead = picklistRenderers.read,
oPickReadonly = picklistRenderers.readonly,
oPickEdit = picklistRenderers.edit;
//Object as switch statement - stores field API names and corresponding snippet names
var globalPickFields = {
'Case_Type__c': 'renderCaseTypes',
'Primary_Phone_Type__c': 'renderPhoneTypes',
'Alternate_Phone_Type__c': 'renderPhoneTypes',
'Interaction_Intention__c': 'renderPregnancyIntentions',
'Pregnancy_Intention__c': 'renderPregnancyIntentions',
'Pregnancy_Intention_on_Intake__c': 'renderPregnancyIntentions',
'Father_of_the_Baby_Intention__c': 'renderPregnancyIntentions',
'Role__c': 'renderStaffRoles',
'Primary_Staff_Role__c': 'renderStaffRoles',
'Additional_Staff_Role__c': 'renderStaffRoles',
'STD__c': 'renderSTDs',
'Sexual_Exposure_to_What_STD__c' : 'renderSTDs',
'STD_Picklist': 'renderSTDs',
'Referral_Source__c': 'renderReferralSources',
'Results__c': 'renderPregnancyTestResults',
'UPT_Results__c': 'renderPregnancyTestResults'
};
// Override all of Skuid's standard Picklist field renderers to call our custom renderer
picklistRenderers.read = picklistRenderers.readonly = picklistRenderers.edit = function(field,value){
       // If the field is one of the global picklist fields, run the snippet for it
if (globalPickFields[field.id]){
//Use a custom renderer
skuid.snippet.getSnippet(globalPickFields[field.id])(field,value);
} else {
//Use the standard renderer
if (field.mode === 'read') {
oPickRead.apply(this,arguments);
} else if (field.mode === 'readonly'){
oPickReadonly.apply(this,arguments);
} else {
oPickEdit.apply(this,arguments);
}
// originalPicklistRenderers[field.mode](field,value);
  }
   };
Photo of J.

J., Official Rep

  • 7,470 Points 5k badge 2x thumb
Matt,

If your custom renderers (e.g. renderCaseTypesrenderPhoneTypes, etc.) themselves point to the standard renderers, I'm pretty sure that's where your infinite loop is getting introduced:
  1. Override standard renderer with custom renderer
  2. Custom renderer calls standard renderer
  3. Standard renderer has been overridden to point to the custom renderer
  4. Customer renderer calls standard renderer
  5. ...
You'll need to pass in the reference to the original versions of those renderers, or use a different renderer (skuid.ui.fieldRenderers.TEXT.readonly might work here).
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Thanks for all the help, J. You're awesome.

I realized that I only really need to override the renderer in edit scenarios, so I ended up with this:

// Get references to our original picklist field renderers	var picklistRenderers = skuid.ui.fieldRenderers.PICKLIST; 
var oPickEdit = picklistRenderers.edit;
//Object as switch statement - stores field API names and corresponding snippet names
var globalPickFields = {
'Case_Type__c': 'renderCaseTypes',
'Primary_Phone_Type__c': 'renderPhoneTypes',
...Lots more options here...
'Referral_Source__c': 'renderReferralSources',
'UPT_Results__c': 'renderPregnancyTestResults'
};
// Override Skuid's standard edit Picklist field renderers to call our custom renderer
picklistRenderers.edit = function(field,value){
       // If the field is one of the global picklist fields, run the snippet for it
if (globalPickFields[field.id]){
//Use a custom renderer
skuid.snippet.getSnippet(globalPickFields[field.id])(field,value);
} else {
//Use the standard renderer
oPickEdit.apply(this,arguments);
  }
   };

And it works!
All of the custom renderer snippets are in the same inline javascript, so they have access to the standard renderer through the oPickEdit variable. As an example, I'm accessing it like so:

'renderReferralSources': function (field, value) {
  if (!picklistRows) getPicklistRows();
var filteredList = picklistRows.filter(function(row){
return row.Picklist__c === 'Referral Sources';
});
   var picklistEntries = [];
   
   $.each(filteredList,function(i,row){
    if (row.Default__c) {
    picklistEntries.unshift(
    { value: row.Picklist_Value__c, label: row.Picklist_Value__c, defaultValue: true, active: true }
    );
    } else {
    picklistEntries.push(
           { value: row.Picklist_Value__c, label: row.Picklist_Value__c, defaultValue: false, active: true }
    );
    }
   });
   field.metadata.picklistEntries = picklistEntries;
   if (field.mode === 'edit') {
oPickEdit.apply(this,arguments);
} else  {
picklistRenderers[field.mode](field,value);
}
}

We have an object which stores custom pickstlist values for certain picklists, each value on its own row. This allows a high degree of picklist customization by users with the appropriate permissions through a skuid interface. Basically, we're using skuid to replicate the "Picklists" metadata that's in beta for SFDC right now, except we're allowing 'administrator' type users to customize the 'metadata' for the picklist values through the skuid ui without touching the salesforce setup back end.


Thanks again for your help!
(Edited)