2019-06-17 12:53 PM - edited 2024-02-06 05:32 PM
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.
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;
var r = /^a$/;
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) {}
try {if (!goFindId) goFindId = $('.SubSectionLabelCollapsible:findField("' + fldName + '")')[0].id;} catch (err) {}
return goFindId ? $LM._layoutItems[goFindId.replace( /^\D+/g, '')].fieldId : 0;
}
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).
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.
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;
var r = /^a$/;
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) {}
try {if (!goFindId) goFindId = $('.SubSectionLabelCollapsible:findField("' + fldName + '")')[0].id;} catch (err) {}
return goFindId ? $LM._layoutItems[goFindId.replace( /^\D+/g, '')].fieldId : 0;
}
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
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.
If you have any issues with this custom object, feel free to create a Discussion in Archer Custom Objects or post your comment below.
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. |
n/a | 02/01/2024 | Updated the link to the Custom Object user group. |
1.3 | 02/6/2024 | Minor change to the lookupFieldId function to include a new (css) style that was introduced when a cross-reference field is located inside a section (with other fields). |