cancel
Showing results for 
Search instead for 
Did you mean: 
100% helpful (4/4)
DavidPetty
Archer Employee
Archer Employee

We've all been there; we package up an application/questionnaire that contains a custom object (or two) from one environment to be installed in another environment (that's the easy part).  The package install goes well and either by checkout testing the application/questionnaire or a barrage of emails/phone calls that the application/questionnaire isn't working anymore and the culprit is that a custom object was referencing field ids that doesn't match the environment anymore, now you have to go tracking down the proper field ids (if it's not documented in the code. hint! hint!) and proceed updating the custom object and hoping a typo or something doesn't break the custom object.

Now if there was only a way these pesky custom objects could dynamically get the field IDs based on the environment, they are running in...

Well, they can, well with a little help and some magic!

I've found a way based on the field name to get the actual field ID of that field dynamically without using any APIs.

Custom Object Configuration

Add the following function to your custom object (only needs to be in one if you have multiple) in each application/questionnaire that has a custom object that uses field ids.

 

function lookupFieldId(fldName){
     var goFindId = null;

     try{
          $('.FieldLabel').each(function(){
               if(($(this).text().indexOf(fldName + ':') != -1) && ($(this).text().indexOf(fldName + ':') == 0)){
                    goFindId = $(this).find("span")[0].id;
                    return false;
               }
          });
     } catch (err) {console.log(err)}
     try {if (!goFindId) goFindId = $('.SectionLabel:findField("' + fldName + '")')[0].id;} catch (err) {}
     try {if (!goFindId) goFindId = $('.SubSectionLabel:findField("' + fldName + '")')[0].id;} catch (err) {}

     return goFindId ? $LM._layoutItems[goFindId.replace( /^\D+/g, '')].fieldId : 0;
}

     $.expr[':'].findField = function(a, i, m) {
     return $(a).text().replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'_').match("^" + m[3].replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'_') + "$");
};

 

You may have to tweak some of you code depending of if you are currently passing an array of field id's (see Advanced Custom Object Configuration on a different approach to getting multiple field IDs).

How to Use

To get the field ID, past the field name [string] to the lookupFieldId(); function, like so:

 

lookupFieldId("Tracking ID");‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Calling the lookupFieldId function and passing "Tracking ID" (this has to match exactly how the field is spelled in Archer) will return the ID for the Tracking ID field.  You could also call the lookupFieldId function as part of defining variables to be use later within the custom object.

 

var TrackingId = lookupFieldId("Tracking ID");‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

The only caveat is that field has to be on the layout or if the field is in a tab, the tab has to be selected.

Advanced Custom Object Configuration

I've also created a way to pass multiple field names at once and the field IDs will be stored in an array that you can reference in your code.  Add the following function to your custom object (only needs to be in one if you have multiple) in each application/questionnaire that has a custom object that uses field ids.

 

function getFieldId(fld){
     var layoutId;
 
     if (typeof fld == 'string' || fld instanceof String) {
          return lookupFieldId(fld)
     } else if (fld.constructor === Array) {
          var fieldDefinitions = {};
          $.each(fld, function(index,value){
               layoutId = lookupFieldId(value);
               layoutId != 0 ? fieldDefinitions[value] = {'id': layoutId} : fieldDefinitions[value] = {'id': 0};
          });
          return fieldDefinitions;
     }
}
 
function lookupFieldId(fldName){
     var goFindId = null;

     try{
          $('.FieldLabel').each(function(){
               if(($(this).text().indexOf(fldName + ':') != -1) && ($(this).text().indexOf(fldName + ':') == 0)){
                    goFindId = $(this).find("span")[0].id;
                    return false;
               }
          });
     } catch (err) {console.log(err)}
     try {if (!goFindId) goFindId = $('.SectionLabel:findField("' + fldName + '")')[0].id;} catch (err) {}
     try {if (!goFindId) goFindId = $('.SubSectionLabel:findField("' + fldName + '")')[0].id;} catch (err) {}

     return goFindId ? $LM._layoutItems[goFindId.replace( /^\D+/g, '')].fieldId : 0;
}

     $.expr[':'].findField = function(a, i, m) {
     return $(a).text().replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'_').match("^" + m[3].replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'_') + "$");
};

 

Then pass the field names like so:

 

var myFields = getFieldId(["Tracking ID","Control Procedures","Control Standards","Policy"]);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Calling the getFieldId function and passing an array of field names [string] (field names has to match exactly how the fields are spelled in Archer) will return an array of all the request fields and there associated ids.

Then in your custom object you can reference the field ID like so:

 

myFields['Policy'].id‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

 

"Re-Usable" Custom Object Configuration

Now if you plan on using this in a lot of applications/questionnaires you might want to put the Advanced Custom Object Configuration version in a .js file and upload it to the company_fields folder\[instance number]\[filename.js] and then just add the following line at the top of one of the custom objects in your application/questionnaire:

 

<script type="text/javascript" src="../company_files/[instance number]/[filename.js]"></script> ‍‍

<!-- Example -->
<script type="text/javascript" src="../company_files/50000/GetFieldIds.js"></script>‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

So if in the future you want to tweak the code or I provide enhancements/bug fixes you don't have to go to every application and make the necessary changes.

This configuration is only for those with running on-prem version of Archer being SaaS customers cannot directly upload non-image files to the company_files folder.

I tested this a lot but we all know that testing ones own code never reveals any major show stoppers.  So enjoy, and hopefully this make life easier moving application/questionnaires with custom object from one environment to another.  Please provide any feedback and any suggestions to make this better.

Archer Versions Supported

  • 6.5.x
  • 6.6.x
  • 6.7.x
  • 6.8.x
  • 6.9.x

Support

If you have any issues with this custom object, feel free to create a Discussion in the RSA Archer Custom Objects  sub-community or post your comment below.

History

Version Date Notes
1.0 08/2/2019 Initial release.
1.1 10/31/2019

Updated the way the field is matched.  The :contains finds the first match so if you had a field name of Contact - Primary and another of Contact - Secondary and if you wanted the field id of Contact - Secondary the code would return Contact - Primary field ID being it found the first hit on "Primary".

1.2 05/24/2021

Minor change to the lookupFieldId function that was preventing it from finding certain field types.

Was this article helpful? Yes No
Version history
Last update:
‎2021-05-24 01:29 PM
Updated by: