Kieran Anson
Kilo Patron

A question that popped up on the SNDevs Slack community was a method to have a select all ability for a list collector within a catalog item.

This can be done via a simple script include and a catalog client script that performs a GlideAjax call.

Script Include

This takes the catalog item sys_id as well as the list field in question. It'll evaluate the reference qualifier as part of the select all process and return the records in a JSON structured object.

var MyCatalogUtils = Class.create();
MyCatalogUtils.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

	getRecords: function() {

		//As the field name isn't necessairly unique
		//pass in the cat item the variable is on
		//Note: This won't work for variable sets - modify to your needs!
		var fieldName = this.getParameter('sysparm_field_name');
		var catItem = this.getParameter('sysparm_cat_item');

		//Look up the variable so we can get the list collector info
		var variableGR = new GlideRecord('item_option_new');
		variableGR.addQuery('name', fieldName);
		variableGR.addQuery('cat_item', catItem);
		variableGR.setLimit(1);
		variableGR.query();
		if (variableGR.next()) {
						
			//Create a new GlideRecord query to the table the variable references
			var tableLookupGR = new GlideRecord(variableGR.getValue('list_table'));

			var encodedQuery = variableGR.getValue('reference_qual');
			tableLookupGR.addEncodedQuery(encodedQuery);

			var returnValues = [];
			tableLookupGR.query();
			while (tableLookupGR.next()) {
				//Push the value and display value to avoid any synchronous display value calls
				//when we populate the list collector on the catalog item
				returnValues.push({
					"value": tableLookupGR.getUniqueValue(),
					"displayValue": tableLookupGR.getDisplayValue()
				});
			}
			return JSON.stringify(returnValues);
		}

	},

	type: 'MyCatalogUtils'
});

We're returning both the value and display value of each record. This avoids ServiceNow performing a synchronous call when we populate the list collector on the catalog item. If we didn't do this, the platform would create a synchronous call for each record populate leading to a poor user experience as the browser window would freeze/hang whilst the synchronous calls complete.

Catalog Client Script

The client script is relatively compact, and only requires one modification which is the list collector field name we're doing the lookup for. For this to run, we're using an onChange catalog client script on a checkbox.

function onChange(control, oldValue, newValue, isLoading) {
	if (isLoading) {
		return;
	}


	var listCollectorName = "FIELD_NAME_HERE";

	if(g_list != "undefined"){
		var listCollector = g_list.get(listCollectorName);
		if(!listCollector)return;
	}

	if(newValue == 'false'){
		g_form.setValue(listCollectorName , '');
		return;
	}

	var ga = new GlideAjax('MyCatalogUtils');
	ga.addParam('sysparm_name' , 'getRecords');
	ga.addParam('sysparm_field_name' , listCollectorName);
	ga.addParam('sysparm_cat_item' , g_form.getUniqueValue());
	ga.getXMLAnswer(function(response){

		if(response == null)return;
		response = JSON.parse(response);

		if(listCollector){
			response.forEach(function(el){
				listCollector.addItem(el.value , el.displayValue);
			});
		} else {
			var values = response.map(function(val){ return val.value;});
			var display = response.map(function(val){return val.displayValue;});
			g_form.setValue(listCollectorName , values.join(",") , display.join(", "));
		}
	});

}

 

find_real_file.png

A special thanks to @Chris Helming for tweaking the code. And Jens for asking the question that got me curious about a solution.

Comments
Maik Skoddow
Tera Patron
Tera Patron

Awesome!

Murthy Ch
Giga Sage

Hi,

Can we use the above code for Native view?

 

 

Kieran Anson
Kilo Patron

Hi Murthy,

Yes this has been set to work in both views

Murthy Ch
Giga Sage

Cool, Thanks for sharing.

I will try this

_ChrisHelming
Tera Guru

I'm famous!

taofeek93
Tera Contributor

@Kieran Anson do you mind sharing the XML for this? I'm having issues replicating it.

Not getting errors, just not working.

 

Thank you

RichardE
Tera Expert

How does that work with the checkbox?  

neil_b
Tera Guru

I've tried this and isn't fully working. All of the values are getting added to the list collector, but my reference qualifier isn't being taken into consideration.

 

Any tips?

 

My reference qualifier for my list collect is "javascript: 'u_location='+current.variables.location;" which is basically filtering my list of users based on the location but it's currently not doing that; it's still selecting all users regardless of location. 

Kieran Anson
Kilo Patron

@neil_b the script provided won't evaluate a reference qualifier with a javascript condition. The logic in the below line is 'dumb' and expects an encoded query string:

 

tableLookupGR.addEncodedQuery(encodedQuery);

 

 You would need to expand the capability provided in this article to cater to that requirement. 

neil_b
Tera Guru

@Kieran Anson I got this one figured out. I had to end up creating another variable and parameter to pull the location from the client script into the script include and from there I was able to get it to work. 

 

For reference, this is the code I added to the client script in bold

    var ga = new GlideAjax('MyCatalogUtils');
    ga.addParam('sysparm_name' , 'getRecords');
    ga.addParam('sysparm_field_name' , listCollectorName);
    ga.addParam('sysparm_cat_item' , g_form.getUniqueValue());
    ga.addParam('sysparm_location' , g_form.getValue('u_location')); // pulls location from record producer so that it is stripped out from the reference qualifier

 

And this is the script include code I added

var location = this.getParameter('sysparm_location'); //pulls location from client script
var encodedQuery = 'u_location='+location; // assigns the encodedQuery variable the correct query string
tableLookupGR.addEncodedQuery(encodedQuery); //utilizes the variable in the addEncodedQuery call
 
neil_b
Tera Guru

Hey @Kieran Anson I have another question for you about this! I noticed that I'm experiencing an issue with this list collector where the select-all won't undo and remove the users from the list.

 

 

Essentially, when the user clicks on "Select All" (setting Select All = True), the respective users get added to the list collector variable as expected. When the user unchecks "Select All" (setting Select All = False), the users get removed from the list collector variable but when the user submits the form, all of the users are still stored with the variable, so the list collector isn't getting completely cleared. It "appeared" that it was getting cleared because the list collector variable would be blank, but upon submission, if you access the native UI and review the request in the backend, the users are in the right slush bucket. I've included a few screen captures below for clarity.

 

Think you could help me figure out why this is happening? TIA!

 

undo not working.png

Kieran Anson
Kilo Patron

@neil_b 

You could try using g_form.clearValue('list_name') to remove the value. Internally it just uses setValue(<field> , "" , "").

neil_b
Tera Guru

@Kieran Anson I gave that a go, and was unsuccessful. I have also tried:

 

    var listCollectorName = 'user_list';
    var listCollector = g_list.get(listCollectorName);
if(newValue == 'false'){
//  g_form.setValue('user_list' , '');    
//  g_form.clearValue('user_list');    
    listCollector.reset();
return;
}

 

Any other suggestions? 
 
 
Version history
Last update:
‎05-03-2022 01:51 AM
Updated by: