Variable Set fields in Classic Catalog not working(g_form.setDisplay() and g_form.setMandatory())
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 weeks ago - last edited 4 weeks ago
Hello Everyone,
I am facing an issue with controlling Variable Set visibility and mandatory fields dynamically using Client Scripts in the Classic Catalog backend view.
I have a Catalog Item with a Variable Set called "Shipping Details"
Internal Name: shipping_address_reclaim_asset
Variable Names: first_name, last_name, street_address, apt_street_other, city, state_province, zip_postal_code.
I have a Script Include that checks if the logged-in user's company has "u_shipping_required = true" in a custom policy table (sn_hamp_territory_procurement). • Based on the result, I want to: → SHOW Variable Set + Make fields mandatory (if shipping required = true) → HIDE Variable Set + Remove mandatory (if shipping required = false)
✅ Script Include is returning correct response (true/false) ✅ GlideAjax call is working correctly ✅ STEP 6 alert fires confirming response = 'true' ✅ Everything works perfectly in ESC Portal ✅ g_form.setDisplay() works on regular catalog variables.
❌ g_form.setDisplay('shipping_address_reclaim_asset', false) → Variable Set still visible for ALL users in backend ❌ g_form.setMandatory('first_name', true) → Fields not showing mandatory (*) indicator in backend.
Script Include:
Client Scripts:
// OnLoad function onLoad()--> UI Type as All
Backend: Fields are not mandatory and variable set showing for all users
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 weeks ago
Hi @sattar3 ,
I have reviewed your problem statement and also done the code analysis, I believe we need to make the variables non mandatory first and then we need to write g_form.setDisplay("variable_ name", false) for each individual variable, then it will work.
function onLoad() { // 1. Get the Sys ID of the 'Requested For' user var reqUserId = g_form.getValue('requested_for'); var ga = new GlideAjax('sn_hamp.ScriptIncludeName'); ga.addParam('sysparm_name', 'checkShippingRequired'); // 2. Pass the user ID to the Script Include ga.addParam('sysparm_user_id', reqUserId); ga.getXMLAnswer(analyzeResponse); } function analyzeResponse(response) { if (response === 'true') { g_form.setDisplay('first_name', true); g_form.setDisplay('last_name', true); g_form.setDisplay('street_address', true); g_form.setDisplay('state_province', true); g_form.setDisplay('city', true); g_form.setDisplay('zip_postal_code', true); g_form.setMandatory('first_name', true); g_form.setMandatory('last_name', true); g_form.setMandatory('street_address', true); g_form.setMandatory('state_province', true); g_form.setMandatory('city', true); g_form.setMandatory('zip_postal_code', true); } else { g_form.setMandatory('first_name', false); g_form.setMandatory('last_name', false); g_form.setMandatory('street_address', false); g_form.setMandatory('state_province', false); g_form.setMandatory('city', false); g_form.setMandatory('zip_postal_code', false); g_form.setDisplay('first_name', false); g_form.setDisplay('last_name', false); g_form.setDisplay('street_address', false); g_form.setDisplay('state_province', false); g_form.setDisplay('city', false); g_form.setDisplay('zip_postal_code', false); } }
I hope this helps! If this solution resolves your issue, please consider marking this answer as Correct✔️.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 weeks ago
Hi @sattar3 ,
Based on your screenshots, the GlideAjax part is working because you are receiving the expected "true" response. I would therefore not troubleshoot the Script Include first.
For this requirement, I would change the design slightly instead of directly controlling the Variable Set with multiple g_form.setDisplay() and g_form.setMandatory() calls.
The more reliable OOB approach across both:
- Classic Catalog / Try It
- Employee Center / Service Portal
is:
GlideAjax
-> Set one helper variable
-> Catalog UI Policy
-> Control Variable Set visibility and mandatory variables
ServiceNow specifically supports Catalog UI Policy Actions against an entire Variable Set, and recommends UI Policies over g_form methods where possible.
1. First verify the Catalog Client Script configuration
For both your onLoad and requested_for onChange scripts verify:
UI Type:
All
Applies on a Catalog Item view:
Checked
The Classic "Try It" page runs in the Desktop/Core UI, while ESC runs in the portal UI. UI Type = All is therefore required.
Since your alert is already firing in Classic, the script itself is loading, but please still confirm "Applies on a Catalog Item view".
2. Create a helper variable
Create a variable on the Catalog Item:
Type:
Single Line Text
Name:
shipping_required_flag
Default:
false
Do NOT set the variable's Hidden checkbox directly.
We will hide it using a Catalog UI Policy because ServiceNow requires variables used in UI Policy conditions to exist on the form; variables hidden by UI Policy can still be evaluated.
3. Create an "Always hide helper variable" Catalog UI Policy
Applies to:
A Catalog Item
Catalog Item:
Your Catalog Item
On load:
True
Reverse if false:
False
Applies on a Catalog Item view:
True
No condition is required.
Create one UI Policy Action:
Variable:
shipping_required_flag
Visible:
False
Mandatory:
Leave alone
Read only:
Leave alone
4. Simplify the onLoad Catalog Client Script
Instead of changing all of the fields from JavaScript, only populate the helper variable.
Use:
function onLoad() {
var reqUserId = g_form.getValue('requested_for');
var ga = new GlideAjax('sn_hamp.ScriptIncludeName');
ga.addParam(
'sysparm_name',
'checkShippingRequired'
);
ga.addParam(
'sysparm_user_id',
reqUserId
);
ga.getXMLAnswer(function(answer) {
answer = (answer || '')
.toString()
.trim()
.toLowerCase();
g_form.setValue(
'shipping_required_flag',
answer == 'true' ? 'true' : 'false'
);
});
}
Replace:
sn_hamp.ScriptIncludeName
with the API name of your actual Script Include.
5. Simplify the requested_for onChange Client Script
Use:
function onChange(
control,
oldValue,
newValue,
isLoading
) {
if (isLoading)
return;
if (!newValue) {
g_form.setValue(
'shipping_required_flag',
'false'
);
return;
}
var ga = new GlideAjax(
'sn_hamp.ScriptIncludeName'
);
ga.addParam(
'sysparm_name',
'checkShippingRequired'
);
ga.addParam(
'sysparm_user_id',
newValue
);
ga.getXMLAnswer(function(answer) {
answer = (answer || '')
.toString()
.trim()
.toLowerCase();
g_form.setValue(
'shipping_required_flag',
answer == 'true' ? 'true' : 'false'
);
});
}
This also fixes one small issue in your current onChange script:
if (isLoading || newValue == '') {
return;
}
When Requested For becomes empty, your current script exits without resetting the previous visibility/mandatory state.
6. Create the Catalog UI Policy that controls Shipping Details
Create another Catalog UI Policy:
Applies to:
A Catalog Item
Catalog Item:
Your Catalog Item
Short description:
Show Shipping Details when shipping is required
On load:
True
Reverse if false:
True
Applies on a Catalog Item view:
True
Condition:
shipping_required_flag
is
true
Then create these UI Policy Actions.
Action 1:
Variable:
Shipping Details [shipping_address_reclaim_asset]
Visible:
True
Mandatory:
Leave alone
Read only:
Leave alone
Then add individual Mandatory actions:
first_name
Mandatory = True
last_name
Mandatory = True
street_address
Mandatory = True
state_province
Mandatory = True
city
Mandatory = True
zip_postal_code
Mandatory = True
Do not make:
apt_street_other
mandatory if that field is optional.
Because:
Reverse if false = True
when shipping_required_flag becomes false, ServiceNow will reverse the actions:
Shipping Details
-> Hidden
Required shipping variables
-> Not mandatory
When it becomes true:
Shipping Details
-> Visible
Required shipping variables
-> Mandatory
7. Why I recommend this over the current script
Your current code is trying to control two different concerns asynchronously:
GlideAjax result
+
UI rendering/mandatory state
Moving the UI behavior into a Catalog UI Policy gives you:
GlideAjax
-> Business decision only
Catalog UI Policy
-> UI behavior only
This is much easier to maintain and troubleshoot across Classic Catalog and ESC.
Also, ServiceNow applies the following priority:
Mandatory
-> Read Only / Display
Therefore, if a mandatory empty variable is still mandatory, ServiceNow will not allow that variable/Variable Set to be hidden.
That is another reason the mandatory and visibility behavior should be managed together through one Catalog UI Policy with Reverse if false.
8. Check for another UI Policy overriding your result
Even if the variables are not mandatory at the variable definition level, another Catalog UI Policy or Catalog Client Script may still be changing them.
From the Classic Catalog:
Maintain Items
-> Open Catalog Item
-> Try It
Enable:
System Settings
-> Developer
-> JavaScript Log and Field Watcher
Right-click one of the variables, for example:
First Name
and select:
Watch Variable
Then change Requested For.
The Variable Watcher will show whether:
- Your Catalog Client Script ran
- A Catalog UI Policy changed Mandatory
- Another policy/script changed it afterward
This is the best way to confirm exactly what is overriding the field.
9. One more improvement to your Script Include
Your current boolean check works, but it can be simplified.
For example:
checkShippingRequired: function() {
var userSysId =
this.getParameter('sysparm_user_id') ||
gs.getUserID();
var userGR = new GlideRecord('sys_user');
if (!userGR.get(userSysId))
return 'false';
var companySysId =
userGR.getValue('company');
if (!companySysId)
return 'false';
var policyGR =
new GlideRecord(
'sn_hamp_territory_procurement'
);
policyGR.addQuery(
'u_company',
companySysId
);
policyGR.setLimit(1);
policyGR.query();
if (!policyGR.next())
return 'false';
return policyGR.getValue(
'u_shipping_required'
) == '1' ? 'true' : 'false';
},
This keeps the Script Include responsible only for returning true/false.
Recommended final design:
Requested For
-> GlideAjax
-> Check user's Company
-> Check u_shipping_required
-> Set shipping_required_flag
-> Catalog UI Policy
-> Show/hide Shipping Details
-> Set/unset mandatory fields
I would use this approach instead of adding DOM manipulation or modifying the Classic Catalog UI Page.
Also do not modify:
com.glideapp.servicecatalog_cat_item_view
to solve this.
Official references:
Service Catalog UI Policies:
https://www.servicenow.com/docs/r/servicenow-platform/service-catalog/c_ServiceCatalogUIPolicy.html
Create Catalog UI Policy:
https://www.servicenow.com/docs/r/servicenow-platform/service-catalog/t_CreatUIPolicyForSvcCalgIt.ht...
Catalog Client Scripts:
https://www.servicenow.com/docs/r/api-reference/scripts/c_CatalogClientScriptCreation.html
Debug Service Catalog Variables:
https://www.servicenow.com/docs/r/servicenow-platform/service-catalog/debug-a-service-catalog-variab...
Hope this helps!
If this response helped, please mark it as Helpful.
If it resolves your issue, please Accept it as Solution.
Kind Regards,
Abhishek Pal
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 weeks ago
Hello @sattar3,
From an architectural perspective, this discrepancy perfectly highlights the fragility of relying on complex client-side scripting to control UI behavior across different rendering engines (Service Portal versus Classic UI).
Whenever we see logic working in the Portal but failing in the backend, it is almost always an indicator that the architecture has strayed too far from the platform's native declarative capabilities. Using g_form methods to dynamically show and hide entire Variable Sets inside an asynchronous GlideAjax callback introduces unnecessary technical debt and risks breaking during future family release upgrades.
To build scalable, upgrade-safe catalog items, I strongly advise decoupling your data retrieval from your UI manipulation. Use your GlideAjax call purely to fetch the data and store that boolean result in a hidden reference variable on the form. From there, leverage native Catalog UI Policies to govern the mandatory and visibility states. Declarative UI Policies are inherently designed by ServiceNow to operate consistently across all interfaces—Workspaces, Portals, and Classic views—ensuring your forms remain resilient and easy to maintain.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 weeks ago
Hi @sattar3,
The classic backend catalog view (UI16) can sometimes be incredibly stubborn when asynchronous GlideAjax callbacks attempt to manipulate container or Variable Set visibility. The issue you are encountering is often caused by scoping loss with the analyzeResponse function sitting outside your main onLoad or onChange blocks, combined with how the classic UI rendering engine handles DOM updates from background scripts.
The most robust, foolproof way to solve this—and vastly simplify your client-side code—is to use the Hidden Helper Variable + UI Policy design pattern:
Create a Hidden Variable: Add a new, hidden Single Line Text variable to your catalog item (e.g., is_shipping_required).
Refactor the Client Script: Change your GlideAjax script to use an inline callback function to completely eliminate scoping issues, and use it strictly to update the hidden variable.
JavaScriptfunction onChange(control, oldValue, newValue, isLoading) { if (isLoading || newValue == '') return; var reqUserId = g_form.getValue('requested_for'); var ga = new GlideAjax('sn_hamp.ScriptIncludeName'); ga.addParam('sysparm_name', 'checkShippingRequired'); ga.addParam('sysparm_user_id', reqUserId); // Use an inline callback ga.getXMLAnswer(function(response) { g_form.setValue('is_shipping_required', response); }); }
Create a Catalog UI Policy: Create a no-code UI Policy with the condition is_shipping_required = true. Check the "Reverse if false" box.
Add UI Policy Actions: Set the shipping_address_reclaim_asset Variable Set to Visible = True, and the individual fields to Mandatory = True.
This forces the platform's native UI engine to handle the visibility and mandatory states, which works flawlessly and consistently across both the Service Portal and the classic backend!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 weeks ago
@Ehab Pilloor @vsrahul127 @Abhishek Pal @VJ_Srivastava @NehaG8791370651 @Ankur Bawiskar Thanks everyone.
Here the issue is with Variable Set scope-previously it is in Asset Management Comm scope.
I created new variable set in Hardware Asset Management scope which is working as expected in both Backend and Frontend.
Thanks,
Sattar
