Populate usernames in to a list collector

Russell Abbott
Kilo Sage

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);
  }
}

 

 

 

1 ACCEPTED SOLUTION

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":

2023-01-10-8.png

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.

View solution in original post

14 REPLIES 14

-O-
Kilo Patron
Kilo Patron

This should go into an on before Business Rule, not an onSubmit client script.

As to the core problem, what you are doing is to overwrite the previously set user 11 times. To set the value of a glide list field should:

- get the existing values into an array

- load the items you want to add to the list into a second array

- create a 3rd array into which you copy unique values from both previous arrays

- set the value of the glide list to the elements of the 3rd array concatenated using the comma character.

Not related, but don't name a field as a u_whatever_name if you are not storing names in it (but list of sys_ids).

For your solution here would I only need 2 arrays?

This process is preceded by another question "do you want some employees? or all?"

 

When the user selects "some" then they can choose the employees from a selected Cost Center (you helped me with that earlier this week!) and those selected employees go in to the List Collector

When the user selects "all", what I'm looking to do is populate the List Collector with all of the employees from the selected Cost Center

 

I read about g_form.addOption but I couldn't get that to work, apparently it's designed to populate a List Collector?

 

g_form.addOption('u_employee_names', users.name);

Actually under the circumstances - where you don't have to worry about mixing in previous values, you need just one array. And no, addOption will not work to add entries to a glide list. It is meant to add options to lookup type of fields.

Back to your requirements, in this case what you need is a GlideAjax call. In a Catalog Client Script configured as an onChange one for fields "Select Cost Center" and "Do you want some employees? or all?". To cover the situation too where a user 1st chooses to add all employees and only after that the user picks the cost center or picks a different cost center.

Do you know how to set up a GlideAjax call?

I don't know much about GlideAjax calls but ill do some reading about that! Ill let you know what I find and where I inevitably get stuck!