- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-04-2023 03:41 PM - edited ‎01-04-2023 03:51 PM
I have an onSubmit client script with which I'm trying to pull the list of usernames from a cost center entered by the user, and populate those names in to a list collector.
The script kind of works. However in the ITIL view I'm only getting 1 entry in the list collector when I know there are 12 or so employees under the selected cost center. From the Portal i'm not getting any entries at all.
Script is as follows
function onSubmit() {
var costCenter = g_form.getValue('u_current_cost_center');
// Get the list of users with the specified cost center
var users = new GlideRecord('sys_user');
users.addQuery('cost_center', costCenter);
users.query();
// Add the employees to the u_employee_names list collector field
while (users.next()) {
g_form.setValue('u_employee_names',users.sys_id);
}
}
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-10-2023 12:38 PM - edited ‎01-15-2023 11:23 PM
In short GlideAjax is a class written by ServiceNow, that leverages the standard XMLHttpRequest - the only way to communicate with a web server after a page is loaded. To be more precise, when I say the only way, I'm talking about XMLHttpRequest.
Anyway, GlideAjax is a client side component that enables talking to the server after page load. It needs a - so called - "handler" on server side - a Script Include, that needs two characteristics:
- to extend class AbstractAjaxProcessor and
- to be marked as "Client callable":
Hint: when creating such Script Includes, always mark checkbox "Client callable" right after entering the Script Include name, before doing any modifications to the Script Include boiler plate code - only in this case the system turns the regular Script Include boiler plate into a GlideAjax suitable Script Include boiler plate.
Next one adds methods that will be "called" by the GlideAjax component; e.g:
var UserAjax = Class.create();
UserAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
'getUsersByCostCenter': function () {
return JSON.stringify([gs.getUserID()]);
},
'type': 'UserAjax',
});
These handler methods should return strings - the only data type that can be transmitted "through the wire" (from server to client). If complex data needs to be sent through, the easiest is to "generate" an object than JSON.stringify() that object, so it becomes a string.
Of course in the final implementation one would load all users belonging to a const center and return an array with their sys_ids - here I am returning an array with a single item, the current user.
On client side, in the onChange event handler one would call this Script Include and its method as below:
function onChange (control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '')
return;
// Indicate which Script Include contains the handler method
var gx = new GlideAjax('global.UserAjax');
// Indicate with method should be called
gx.addParam('sysparm_name', 'getUsersByCostCenter');
// Add a value to the payload - in this case the id of the Cost Center
// selected - it is assumed that this onChange event handler is for the
// variable in which the Cost Center is selected
gx.addParam('sysparm_value', newValue);
// Initiate the Ajax call indicating which function to call when the data
// from server is received by the browser
gx.getXMLAnswer(receiveUsersByCostCenter);
}
function receiveUsersByCostCenter (payload) {
// Turn the string sent by the server back into an object/array
var users = JSON.parse(payload);
g_form.setValue('<glide list variable name>', users.join(','));
// Probably:
//g_form.setValue('u_employee_names', users.join(','));
}
Well, I suppose that's actually usable already, only thing needed is to plug in the correct variable name where the setValue method is called.
Server sides in the Script Include created, in the method called, one can use this.getValue() to get to the data sent over from the client side (set by instruction gx.addParam('sysparm_value', newValue);):
var UserAjax = Class.create();
UserAjax.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
'getUsersByCostCenter': function () {
var gr = new GlideRecord('sys_user'),
users = [];
// Validate and set the query in one go
if (gr.isEncodedQueryValid('cost_center=' + this.getValue())) {
gr._query();
// Add selected users to the array which will be sent to the
// client side
while (gr._next())
users.push(gr.getUniqueValue());
}
// Return the list of users retrieved or an empty list if the user
// lookup did not succeed for some reason
return JSON.stringify(users);
},
'type': 'UserAjax',
});
Well, actually this should already solve the requirement. But note that this is not secure at all: anyone can "manually" call the same method, so you should add protection to method getUsersByCostCenter to only return data if the user qualifies, e.g. has a specific role. Unless you don't care whether any authenticated user can or cannot get the list of users belonging to any of the Cost Centers.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-10-2023 12:38 PM - edited ‎01-15-2023 11:23 PM
In short GlideAjax is a class written by ServiceNow, that leverages the standard XMLHttpRequest - the only way to communicate with a web server after a page is loaded. To be more precise, when I say the only way, I'm talking about XMLHttpRequest.
Anyway, GlideAjax is a client side component that enables talking to the server after page load. It needs a - so called - "handler" on server side - a Script Include, that needs two characteristics:
- to extend class AbstractAjaxProcessor and
- to be marked as "Client callable":
Hint: when creating such Script Includes, always mark checkbox "Client callable" right after entering the Script Include name, before doing any modifications to the Script Include boiler plate code - only in this case the system turns the regular Script Include boiler plate into a GlideAjax suitable Script Include boiler plate.
Next one adds methods that will be "called" by the GlideAjax component; e.g:
var UserAjax = Class.create();
UserAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
'getUsersByCostCenter': function () {
return JSON.stringify([gs.getUserID()]);
},
'type': 'UserAjax',
});
These handler methods should return strings - the only data type that can be transmitted "through the wire" (from server to client). If complex data needs to be sent through, the easiest is to "generate" an object than JSON.stringify() that object, so it becomes a string.
Of course in the final implementation one would load all users belonging to a const center and return an array with their sys_ids - here I am returning an array with a single item, the current user.
On client side, in the onChange event handler one would call this Script Include and its method as below:
function onChange (control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '')
return;
// Indicate which Script Include contains the handler method
var gx = new GlideAjax('global.UserAjax');
// Indicate with method should be called
gx.addParam('sysparm_name', 'getUsersByCostCenter');
// Add a value to the payload - in this case the id of the Cost Center
// selected - it is assumed that this onChange event handler is for the
// variable in which the Cost Center is selected
gx.addParam('sysparm_value', newValue);
// Initiate the Ajax call indicating which function to call when the data
// from server is received by the browser
gx.getXMLAnswer(receiveUsersByCostCenter);
}
function receiveUsersByCostCenter (payload) {
// Turn the string sent by the server back into an object/array
var users = JSON.parse(payload);
g_form.setValue('<glide list variable name>', users.join(','));
// Probably:
//g_form.setValue('u_employee_names', users.join(','));
}
Well, I suppose that's actually usable already, only thing needed is to plug in the correct variable name where the setValue method is called.
Server sides in the Script Include created, in the method called, one can use this.getValue() to get to the data sent over from the client side (set by instruction gx.addParam('sysparm_value', newValue);):
var UserAjax = Class.create();
UserAjax.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
'getUsersByCostCenter': function () {
var gr = new GlideRecord('sys_user'),
users = [];
// Validate and set the query in one go
if (gr.isEncodedQueryValid('cost_center=' + this.getValue())) {
gr._query();
// Add selected users to the array which will be sent to the
// client side
while (gr._next())
users.push(gr.getUniqueValue());
}
// Return the list of users retrieved or an empty list if the user
// lookup did not succeed for some reason
return JSON.stringify(users);
},
'type': 'UserAjax',
});
Well, actually this should already solve the requirement. But note that this is not secure at all: anyone can "manually" call the same method, so you should add protection to method getUsersByCostCenter to only return data if the user qualifies, e.g. has a specific role. Unless you don't care whether any authenticated user can or cannot get the list of users belonging to any of the Cost Centers.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-11-2023 01:48 PM
I'm still working through this, man I wish ServiceNow had a tipping method. I appreciate your work here, you're miles head of where I'm at with coding! Ill reply with my findings after I'm done tinkering here.
Thanks again!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-11-2023 02:01 PM
You're welcome 🙂 Hope you figure it out, if not, just tell us where you got stuck.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-15-2023 11:27 PM
This worked better than I could have expected! When I select the Cost Center and then go on to 'Ill provide the employee names', the List Collector is populated already with the users from that CC and all the Requestor needs to do is click (x) on the ones they do not want.
Q) What's happening behind the scenes when I adjust the name of the script include in the Client Script, on this line
/ Indicate which Script Include contains the handler method
var gx = new GlideAjax('global.och_cc_userAjax');
When I type in the name, it turns into Italics, Is it telling me 'yes this is a Script include name', so it's querying the Script includes as I'm typing in here?
I don't know how to thank you, but thank you for your time and effort in helping me learn. Ill keep this thread stickied because I'm sure ill be using similar functionality in other Requested Cat Items
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-15-2023 11:32 PM
You're welcome! 🙂
You're right, since this is the "standard" method to dynamically update a page after load, to fetch new data that is, you will for sure be writing GlideAjax calls and matching Script Includes.
As for turning the Script Include name into italics, you're right, it is a functionality of the editor, where it recognizes Script Include "references" and if you right-click on it and wait a couple of seconds, a context menu will pop up, allowing you to jump the the definition of it, the Script Include record itself: