Multiple instances of custom field renderer on page - only first waits for dynamic model.

  • 1
  • Problem
  • Updated 3 years ago
This is a bit complex, so bear with me.

Ben (awesomely) helped correct some issues with this combination of custom field renderer and dynamically created model.

This field renderer is running five times on the same page, hence the switch/case statement. The problem is that apparently only the first instance is waiting for the Nurse model to be created. The subsequent models seem to think that the Nurse model exists, and run the rendering logic immediately. However, there is nothing in the Nurse model, so their picklists have nothing in them.

What's the best way to approach a solution? Do I have to separate these into individual field renderers? It seems like they would run into the same problem of the latter ones assuming that the Nurse model exists when it doesn't. Perhaps my problem is that a new deferred promise is being created for each instance of the field renderer? I'm still fuzzy on the jquery deferred.

Enough speculation. Here's the code:

custom field renderer:

'renderInitials': function (field, value) { value = skuid.utils.decodeHTML(value);
// Helper function with all rendering logic
var renderingLogic = function() {
if (field.mode === 'edit'){
   var initials = [],
       currentRN = value,
       renderer = false,
       userId = skuid.utils.userInfo.userId;
   
   //get options for initials picklist    
   $.each(nurseModel.getRows(), function(i, row){
       initials.push({
           value: row.Initials__c,
           label: row.Initials__c
       });
       if (!currentRN && (row.Id == userId)) {currentRN = row.Initials__c}
   });
   
   //determine if we should set the default initials
   switch (field.id) {
       case 'RN_Initials_for_Test__c' :
           renderer = (field.model.getFieldValue(field.row, 'STD_Test_Method__c')) ? true : false;
           break;
       case 'RN_Initials_for_Results__c' :
           renderer = (field.model.getFieldValue(field.row, 'Results__c')) ? true : false;
           break;
       case 'RN_Initials_for_Reporting_to_Patient__c' :
           renderer = (field.model.getFieldValue(field.row, 'Results_Reported_to_Patient__c')) ? true : false;
           break;
       case 'RN_Initials_Reporting_to_Health_Dept__c' :
           renderer = (field.model.getFieldValue(field.row, 'Results_Reported_to_Health_Department__c')) ? true : false;
           break;
       case 'RN_Initials_for_Treatment__c' :
           renderer = (field.model.getFieldValue(field.row, 'STD_Treatment__c')) ? true : false;
           break;
   }
   
   //render picklist
   var customSelect = skuid.ui.renderers.PICKLIST.edit({
               entries : initials,
               required : false,
               defaultValue : (renderer) ? currentRN : ''
           }).change(function() {
               //  Update the row in the target object
               field.model.updateRow(field.row, field.id, $(this).val());
           });
   field.element.append(customSelect);
} else {
   skuid.ui.fieldRenderers.TEXT[field.mode](field,value);
}
};
// Get the Nurses Model
var nurseModel = skuid.$M('Nurses');
if (!nurseModel) { // If it doesn't exist, create it.
var runCreateNurseModel = skuid.snippet.getSnippet('createNurseModel');
var waitForModel = runCreateNurseModel();
$.when(waitForModel).then(function(resolvedModel){
nurseModel = resolvedModel;
// Render!
renderingLogic();
});
} else {
// Render!
renderingLogic();
}
}

Dynamic creation of model:

'createNurseModel': function() { // jQuery deferred promise
dfd = new $.Deferred();
// Create dynamic model to capture nurse
nurseModel = new skuid.model.Model();
nurseModel.objectName = 'User';
nurseModel.id = 'Nurses';
nurseModel.fields = [
   { id: 'Id' },
   { id: 'Initials__c' }
];
nurseModel.orderByClause = 'Initials__c'
nurseModel.conditions = [
   { 
       type: 'fieldvalue', 
       field: 'UserRole.Name', 
       operator: 'contains', 
       value: 'RN',
       enclosevalueinquotes: true
   }
];
$.when(nurseModel.initialize().register().load())
.done(function(){
dfd.resolve(nurseModel);
})
.fail(function(){
dfd.reject();
});
return dfd.promise();
}

The first picklist renders like this (and obviously takes a second after page load to show up):


The next four render immediately on page load like this:
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb

Posted 3 years ago

  • 1
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
It seems like the challenge is this:

How to I get all the field renderers to wait for the model to be loaded without asking every one of them to re-create the model?

Do I have to use a settimeout for 200ms or something at the beginning of the rendering logic to keep cycling until the model has rows?
Photo of Zach McElrath

Zach McElrath, Employee

  • 49,056 Points 20k badge 2x thumb
One problem may be that each of these field renderers is independently trying to dynamically create the Model, and they may be interfering with each other in this effort. To correct this you can adjust your createNurseModel function to see if the process of creating a nurse model is already in motion, and if so rather than starting a duplicate iteration of this process, return the same exact promise. Example:


var createNurseModelDfd;

'createNurseModel': function() { // jQuery deferred promise
// If we are already in process of creating a nurse model deferred, return the same promise
if (createNurseModelDfd) return createNurseModelDfd.promise();
// Otherwise proceed with creation of the deferred and the dynamic model 
var dfd = createNurseModelDfd = new $.Deferred();

// Create dynamic model to capture nurse
// ....
Photo of Matt Sones

Matt Sones, Champion

  • 31,478 Points 20k badge 2x thumb
Zach,

Thanks for the help. Your code, combined with checking for the existence of rows along with the existence of the model, and we're good to go!

if (nurseModel && nurseModel.getRows().length) {
renderingLogic();
} else {
var runCreateNurseModel = skuid.snippet.getSnippet('createNurseModel');
var waitForModel = runCreateNurseModel();
$.when(waitForModel).then(function(resolvedModel){
nurseModel = resolvedModel;
// Render!
renderingLogic();
});
}