Contents
In How to convert UI Scripts to run in Configurable Workspaces, we learned how to create UI Scripts that work in Configurable Workspaces (WEP) and Service Portal. The article was so big I had to split it into two! In this blog, you will learn how to write UI Scripts that work in both WEP and Classic UI.
UI Script
In my last article, we copied an existing UI Script made for the Classic UI and made it compatible with WEP. For reference, the script is shown below:
(function() { var Greetings = function(/*Your globals here */g_form, g_user) { var sn_nerd = sn_nerd || {}; sn_nerd.greetings = (function() { function getFullName() { return g_user.getFullName(); } return { hello: function() { g_form.addInfoMessage("Hello " + getFullName()); }, goodbye: function() { g_form.addInfoMessage("Goodbye " + getFullName()); }, type:'Greetings' }; })(); return sn_nerd; }; function initialiseScript(g_form, g_user) { // Return intialise script to client script so globals can be passed in globalThis.sn_nerd = Greetings(g_form, g_user); } return initialiseScript; })();
This script does not work in Classic UI. Let’s modify it so it works in both.
First, we must change the UI Type to All to make our UI Script cross-compatible with Classic UI.
‘this’ is confusing
We need to initialise our script differently for Classic UI and WEP.
For WEP, we use globalThis
to make our API accessible to all Client Scripts and return it to initialise and pass in our globals that are not accessible in UI Scripts for WEP.
function initialiseScript(g_form, g_user) { // Return intialise script to client script so globals can be passed in globalThis.sn_nerd = Greetings(g_form, g_user); } return initialiseScript;
We don’t need to return the initialiser function or pass in our globals in Classic UI as we already have access to them.
But how do we tell from the UI Script if we are in Classic UI or WEP?
Let’s log out the differences for this
, global
and globalThis
as shown below:
(function(global) { // Log object to console to see the difference console.log('this'); console.log(this); console.log('global'); console.log(global); console.log('globalThis'); console.log(globalThis); /* Your UI Script Code here */ })(this);
The console outputs are as follows:
In our UI Script we can, therefore determine if we are running in the Classic UI or WEP by checking global to see if it is a function or an object.
var isClassicUI = (typeof global == 'object'); if (isClassicUI) { initialiseScript(g_form, g_user); } else { // In Configurable Workspaces (WEP) UI Scripts run in sandbox without access to globals such as g_form and g_user // Return the initialise function so it can be used in the calling script to pass the globals return initialiseScript; } function initialiseScript(g_form, g_user) { globalThis.sn_nerd = Greetings(g_form, g_user); }
You could also do this by checking if other globals, such as g_form
and g_user
are defined.
(function() { var Greetings = function(/*Your globals here */g_form, g_user) { // Original UI Script code here }; var isClassicUI = (typeof g_form != 'undefined' && typeof g_user != 'undefined'); if (isClassicUI) { initialiseScript(g_form, g_user); } else { // In Configurable Workspaces (WEP) UI Scripts run in sandbox without access to globals such as g_form and g_user // Return the initialise function so it can be used in the calling script to pass the globals return initialiseScript; } function initialiseScript(g_form, g_user) { globalThis.sn_nerd = Greetings(g_form, g_user); } })();
I would probably do the above as it is less confusing, but it was interesting to explore how UI Scripts differ with my first method.
Consolidate
Because g_user.fullName
is not available in the WEP version of GlideForm, we are opting to use an available function. In the event that you are calling a function not available to both API’s, you can try both of them.
return g_user.getFullName(); // Used by WEP and Classic UI return g_user.fullName || g_user.getFullName(); // If fullName fails, try getFullName() // Check if methods are undefined if (typeof g_user.fullName != 'undefined') { return g_user.fullName; } else if (typeof g_user.getFullName != 'undefined') { return g_user.getFullName(); } else { return 'Guest'; }
Our final UI Script looks like this:
(function() { var Greetings = function(/*Your globals here */g_form, g_user) { var sn_nerd = sn_nerd || {}; sn_nerd.greetings = (function() { function getFullName() { return g_user.getFullName(); } return { hello: function() { g_form.addInfoMessage("Hello " + getFullName()); }, goodbye: function() { g_form.addInfoMessage("Goodbye " + getFullName()); }, type:'Greetings' }; })(); return sn_nerd; }; var isClassicUI = (typeof g_form != 'undefined' && typeof g_user != 'undefined'); if (isClassicUI) { initialiseScript(g_form, g_user); } else { // In Configurable Workspaces (WEP) UI Scripts run in sandbox without access to globals such as g_form and g_user // Return the initialise function so it can be used in the calling script to pass the globals return initialiseScript; } function initialiseScript(g_form, g_user) { globalThis.sn_nerd = Greetings(g_form, g_user); } })();
Client Script
Similar to the UI Script, we must change the UI Type to All.
Depending on the UI, we need to use the appropriate function to load in UI scripts.
function onLoad() { var scriptName = "Greetings"; if (typeof g_ui_scripts != "undefined") { g_ui_scripts.getUIScript(scriptName).then(function(initScript) { initScript(g_form, g_user); sayHello(); }); } else { ScriptLoader.getScripts(scriptName + ".jsdbx", sayHello); } function sayHello() { sn_nerd.greetings.hello(); } }
Chain loading
Unlike ScriptLoader
, g_ui_scripts
API can only load one UI Script at a time. If you need to load in multiple UI Scripts and run some wrap-up code, you can chain-load them using the following pattern:
var scripts = ['Script1', 'Script2', 'Script3']; // Chain loading of scripts function loadScript(loadScripts) { g_ui_scripts.getUIScript(loadScripts[0]).then(function(script) { console.log('Script ' + loadScripts[0] + ' loaded'); var nextScript = loadScripts.shift(); //Remove first element if (typeof nextScript != 'undefined') { console.log('Attempt to load next script ' + loadScripts[0]); loadScript(loadScripts); } else { allScriptsLoaded(); } }); } function allScriptsLoaded() { console.log("Scripts Loaded"); } loadScript(scripts);
TLDR
If you do not want to manage separate Client Scripts and UI Scripts for WEP and Classic UI, you can combine them. You need to:
- Change Client Script and UI Script UI Type to All
- Handle UI Script Initialisation based on UI Type
- Return an initialiser function to set globals for WEP
- Use globalThis for both
- Handle UI Script loading in Client Script based on UI Type
ScriptLoader
for Classic UIg_ui_scripts
for WEP- Chain load for multiple with dependencies
- Consider and handle any API differences for global objects
g_form
andg_user
may have different methodsg_user_date_time_format
for Classic UIg_date_time_format
for WEP- Be wary of other differences that may occur.
While it takes more effort to create scripts for compatibility, if you manage forms in Classic UI and WEP, you can use the above techniques to combine your scripts to make them easier to manage.