S
ervice catalog variables can be a challenge to deal with on standard forms when they are displayed in a variable editor. I’ve written before about different ways that you can solve one of these challenges…making the variables read only, so that they can’t be modified after the initial submission through the service catalog interface. Another common problem I’ve seen is that you can end up with a lot of variables that end up empty in the variable editor on your request item or task because they were optional or hidden on the front-end catalog form. If the variables are empty and you aren’t going to have users interact with them on the standard forms then there isn’t much use in having these variables show up at all in the variable editor.
Until now there really hasn’t been a good way to deal with this issue because of the challenges of dealing with so many different variable types in client-side JavaScript. A couple of days ago one of my colleagues, Jacob Kimball, suggested to me that we might be able to overcome this issue by using a ‘display’ business rule to collect the blank variable information at the server and then pass those variable names to the client. So, you can thank Jacob Kimball for the brilliance of this solution. I’m just spreading the love. ![]()
As explained above, the key to making this work is a ‘display’ business rule. The business rule runs before the display of any record in the table (tasks in this case) and queries the ‘sc_item_option_mtom’ and ‘question_answer’ tables to collect any variable names for empty variables. Then it passes this information in the ‘g_scratchpad’ object to the client to hide the variables on the form.
Here is how you could set up the business rule. The script is designed to hide any empty variables for any task records whether they are generated from a record producer or as a catalog item.
Name: Hide Empty Variables
Table: Task
When: display
Condition: !RP.isPopup()
Script:
g_scratchpad.emptyVars = '';
//Check to see if a variable pool exists
var count = 0;
for(vars in current.variable_pool){
count++;
break;
}
//If a variable pool exists then collect empty variable names
if(count > 0){
var emptyVars = new Array();
var table = current.getTableName();
//Query for the empty variables for this record
//Catalog item and task variables pull from 'sc_item_option_mtom' table
if(table == 'sc_req_item' || table == 'sc_task'){
var itemVars = new GlideRecord('sc_item_option_mtom');
if(table == 'sc_req_item'){
itemVars.addQuery('request_item', current.sys_id);
}
if(table == 'sc_task'){
itemVars.addQuery('request_item', current.request_item.sys_id);
}
itemVars.addNullQuery('sc_item_option.value');
//Exclude Label and Container variables
itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 11);
itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 19);
itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 20);
itemVars.query();
while(itemVars.next()){
//Add variable names to the emptyVars array
emptyVars.push(itemVars.sc_item_option.item_option_new.name.toString());
}
}
else{
//All other variables pulled from 'question_answer' table
var producerVars = new GlideRecord('question_answer');
producerVars.addQuery('table_sys_id', current.sys_id);
producerVars.addNullQuery('value');
//Exclude Label and Container variables
producerVars.addQuery('question.type', '!=', 11);
producerVars.addQuery('question.type', '!=', 19);
producerVars.addQuery('question.type', '!=', 20);
producerVars.query();
while(producerVars.next()){
//Add variable names to the emptyVars array
emptyVars.push(producerVars.question.name.toString());
}
}
//Store the result in the scratchpad
g_scratchpad.emptyVars = emptyVars.join();
}
Once you’ve got the empty variable names collected all you have to do is set up a client script to grab the ‘g_scratchpad’ variable, split out any empty variable names, and hide each one. The client script is pretty simple since the heavy lifting is being done in the business rule. Just make sure that you check the ‘Inherited’ checkbox if you decide to set this up on the task table!
Name: Hide Empty Variables
Type: OnLoad
Table: Task
Inherited: True
Script:
//Hide all empty variables using the scratchpad object passed from 'Hide Empty Variables' business rule
if(g_scratchpad.emptyVars != ''){
var emptyVars = g_scratchpad.emptyVars.split(',');
for(i = 0; i < emptyVars.length; i++){
eval("g_form.setDisplay('variables." + emptyVars[i] + "', false)");
}
}
}
Comments
Posted On
Jun 16, 2011Posted By
Abhiram BharadwajBrilliant !
Thanks a lot Mark !
Posted On
Jun 16, 2011Posted By
Steve DarityThat is Very Cool. Just recently learned about ‘Display’ Business Rules. This functionality should be a user configurable property setting in Service Now.
Thank You for the Contribution.
Posted On
Jun 21, 2011Posted By
MarkOne thing to watch out for are any fields that are still marked as mandatory, if the fields get hidden by the client script, the record may not be able to be modified.
Posted On
Jul 15, 2011Posted By
Matt BensonLove this functionality. My users certainly enjoy it.
I would love to be able to extend this sort of functionality to email notifications. Specifically approval requests. Currently all variables show up in those notifications. It would be nice to not include those that are empty.
Posted On
Jul 18, 2011Posted By
Mark StangerThis is certainly possible, but it depends on how you are including those variable values in your email. You would have to add or modify a mail script for that notification to check each variable as it gets added to the notification and make sure that it has a value.
Posted On
Jul 18, 2011Posted By
Matt BensonWe are using the Summary of Requested Items mail script from the wiki.
http://wiki.service-now.com/index.php?title=Scripting_for_Email_Notifications#Summary_of_Requested_Items
Posted On
Jul 27, 2011Posted By
MarkThis is what we use in our email template which may help with not passing empty variables
template.print("<i>" + gs.getProperty("glide.servlet.uri") + "</i>: \n");
var desc = current.sysapproval.short_description;
var number = current.sysapproval.number;
template.print("\n");
template.print("Request Item " + number + ":" + desc + " \n");
for (key in current.sysapproval.variables) {
var v = current.sysapproval.variables[key];
if(v.getGlideObject().getQuestion().getLabel() != '') {
if(v.getDisplayValue() != 'false'){
if(v.getDisplayValue() != ''){
if (v != "" && v != null && typeof(v) != "undefined") {
template.space(4);
template.print('' + v.getGlideObject().getQuestion().getLabel() + ": " + v.getDisplayValue() + " \n");
}
}
}
}
}
template.print("\n");
Posted On
Jul 28, 2011Posted By
Mark StangerAwesome! Great example. For the benefit of others, the ‘if(v.getDisplayValue…’ lines in the middle of the script are the part that filters out the empty variables. Thanks for sharing!
Posted On
Oct 27, 2011Posted By
rohanaHi Mark, thanks for sharing this. How to hide those
1. What are 11, 19, 20 meaning in > producerVars.addQuery(‘question.type’, ‘!=’, 19)?
2. How to hide variables that already set to visible = false but has pre-defined value such as ‘No’, ‘None’, ’1′. They are still appearing although the visibility has been set to false during the form-filling process.
appreciate your help on this!
Posted On
Oct 27, 2011Posted By
Mark StangerThanks. Those numbers correspond to specific variable types (labels and containers) that we don’t want included in the query because they should not be hidden.
Regarding hiding variables with a matching default value, you can modify the script to do this, but I think it will be difficult to determine which items to really hide. You may have some cases where an un-changed default value might actually be useful information. Any ‘visible = false’ setting that you’ve performed in the catalog or variable setup won’t transfer well to the back-end forms…which is why the script is necessary.
If you want to try hiding variables whose default values have not been changed, then you can modify the script like this…
…For the ‘itemVars’ while loop…
if(itemVars.sc_item_option.item_option_new.default_value != itemVars.sc_item_option.value){
//Add variable names to the emptyVars array
emptyVars.push(itemVars.sc_item_option.item_option_new.name.toString());
}
…And within the ‘producerVars’ while loop…
//Exclude any variables whose value matches their default value
if(producerVars.question.default_value != producerVars.value){
//Add variable names to the emptyVars array
emptyVars.push(producerVars.question.name.toString());
}
Posted On
Dec 08, 2011Posted By
Josh BI’ve modified this a bit to hide variables, labels, containers and UI pages that are not set to Global. However, it doesn’t seem to work with containers that are set to be “two columns wide”. Works find for one column layouts. I’ve confirmed that it’s not the business rule. Seems to be something with the setDisplay function. Here’s the modified version of the script, if anyone is interested:
g_scratchpad.emptyVars = '';
//Check to see if a variable pool exists
var count = 0;
for(vars in current.variable_pool){
count++;
break;
}
//If a variable pool exists then collect empty variable names
if(count > 0){
var emptyVars = new Array();
var table = current.getTableName();
//Query for the empty variables for this record
//Catalog item and task variables pull from 'sc_item_option_mtom' table
if(table == 'sc_req_item' || table == 'sc_task'){
var itemVars = new GlideRecord('sc_item_option_mtom');
itemVars.addQuery('request_item', (table == 'sc_req_item' ? current.sys_id : current.request_item.sys_id));
var q1 = itemVars.addNullQuery('sc_item_option.value');
//Include variables not defined as global
q1.addOrCondition('sc_item_option.item_option_new.global', 'false');
itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 11);
itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 15);
itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 19);
itemVars.addQuery('sc_item_option.item_option_new.type', '!=', 20);
itemVars.query();
while(itemVars.next()){
//Add variable names to the emptyVars array
emptyVars.push(itemVars.sc_item_option.item_option_new.name.toString());
}
//This is for checkboxes
itemVars = new GlideRecord('sc_item_option_mtom');
itemVars.addQuery('request_item', (table == 'sc_req_item' ? current.sys_id : current.request_item.sys_id));
itemVars.addQuery('sc_item_option.item_option_new.type', 7);
itemVars.addQuery('sc_item_option.value', 'false');
itemVars.query();
while(itemVars.next()){
//Add variable names to the emptyVars array
emptyVars.push(itemVars.sc_item_option.item_option_new.name.toString());
}
//This is for labels, containers and UI pages
itemVars = new GlideRecord('sc_item_option_mtom');
itemVars.addQuery('request_item', (table == 'sc_req_item' ? current.sys_id : current.request_item.sys_id));
itemVars.addQuery('sc_item_option.item_option_new.type','IN','11,15,19,20');
itemVars.addQuery('sc_item_option.item_option_new.global', 'false');
itemVars.query();
while(itemVars.next()){
//Add variable names to the emptyVars array
emptyVars.push(itemVars.sc_item_option.item_option_new.name.toString());
}
}else{
//All other variables pulled from 'question_answer' table
var producerVars = new GlideRecord('question_answer');
producerVars.addQuery('table_sys_id', current.sys_id);
producerVars.addNullQuery('value');
//Exclude Label and Container variables
producerVars.addQuery('question.type', '!=', 11);
producerVars.addQuery('question.type', '!=', 19);
producerVars.addQuery('question.type', '!=', 20);
producerVars.query();
while(producerVars.next()){
//Add variable names to the emptyVars array
emptyVars.push(producerVars.question.name.toString());
}
}
//Store the result in the scratchpad
g_scratchpad.emptyVars = emptyVars.join();
}
Posted On
Dec 08, 2011Posted By
Mark StangerThanks Josh!
Posted On
Dec 12, 2011Posted By
PaulMark,
I have a need to edit some variables at certain stages of the workflow once the item is created.
I am trying to modify this script to allow enabling and disabling mandatory for the variables based on another flag set on the sc_task record. I modified the push line to check for mandatory attribute but the reference doesn’t hold up. Any advice?
if(itemVars.sc_item_option.sc_cat_item_option.mandatory=='true') {
mandatoryVars.push(itemVars.sc_item_option.item_option_new.name.toString());
}
Posted On
Dec 12, 2011Posted By
Mark StangerHey Paul,
I think the problem is in your ‘if’ statement. If you walk through that reference, you’ll notice that ‘sc_cat_item_option’ is empty. I think if you replace that with ‘item_option_new’ then it will work better.
Posted On
Dec 14, 2011Posted By
PaulThanks Mark,
I found that just about the same time I saw your post. My next question… How can I make this work for Record Producers?
It appears that sc_item_option_mtom only works for sc_req_item. Can you point me in the right direction?
Posted On
Dec 14, 2011Posted By
Mark StangerThat’s handled in the ‘else’ statement in the business rule script above. Record producer variables are stored in the ‘question_answer’ table.
Posted On
May 11, 2012Posted By
Wendy HHas anyone had any issues with this with the Aspen Hotfixes? We applied the Hotfixes on QA to 04_25_2012_1204 last week and ever since we applied the patch I can no longer run the BR – It hangs all browsers and says due to long running script that the page won’t load. Is there a fix for this?
Posted On
May 12, 2012Posted By
Mark StangerI haven’t seen or heard of this issue. The only thing I can suggest would be to confirm which part of the business rule is causing the problem by taking pieces out and replacing them with ‘gs.log’ entries until you identify the issue. If you’re going to K12 I could take a look at it in person.