In a Client Script, how to async await a return from a GlideAjax call made in a UI script function?

JeremiP
Tera Guru

Hi!

As a follow to my previous topic: "Script Includes but for Client-side code?"

https://www.servicenow.com/community/developer-forum/script-includes-but-for-client-side-code/m-p/27...

 

I've started trying to implement my use case:

 

On a Catalog Item with three variables:

- VAT - read-write, number (by regex)

- Netto - read-write, number (by regex)

- Brutto - read-only, to be written to by g_form.setValue/else

I want to have the value for Brutto Var. be calculated by a GlideAjax script whenever either VAT or Netto change their values.

 

I can use two onChange Client Scripts to do that, but I like the "Don't Repeat Yourself" rule. So I thought I could try a challenge: how to not repeat the same GlideAjax call twice?

So thanks to @Sandeep Rajput 's suggestion, I have found that I can use a UI script for this purpose.

 

My plan would be to do the following:

 

  1. The onChange Catalog Client Script triggers:
  2. This Client Script would call the UI Script,
  3. The UI Script would in turn make a GlideAjax call to a Client-callable Script Include,
  4. The Script Include function returns the value to the UI Script via .getXMLAnswer,
  5. The UI Script would return the value to the Catalog Client Script, which would use g_form.setValue to set the value of the Brutto variable
    1. alternatively, the UI Script sets the value for Brutto on the Service Portal/backend Catalog Item request form

 

My current setup is approximately the following:

1. onChange Catalog Client Script on VAT/Netto executes

1a. The Client Script checks whether both the fields have a valid value

2. The CS makes a call to the GlideUIScripts API:

g_ui_scripts.getUIScript('VatCalcUiScript').then(function (uiscript) {
var calculatedBrutto = uiscript.calculateBrutto(parameters...);
console.debug('GlideUIScripts: calculatedBrutto=' + calculatedBrutto);
...
}

3. The VatCalcUtil UI Script checks whether the provided arguments are correct, and makes a GlideAjax call:

			var ga = new GlideAjax('VatCalcUtils');
			ga.addParam('sysparm_name', 'calculateBrutto');
			ga.addParam(...)
			console.debug('calculateBrutto: calling GlideAjax');
			ga.getXMLAnswer(function (answer) {
				var parsedAnswer = JSON.parse(answer);
				console.debug('calculateBrutto: parsedAnswer = ' + parsedAnswer);
				if (isNaN(parsedAnswer) || !answer) {
					console.debug("calculateBrutto: Error encountered. Return is: " + answer);
					return false;
				}
				console.debug('calculateBrutto: returning...');
				return parseFloat(parsedAnswer);
}

 

But soon I encounter a perhaps obvious issue... The GlideAjax.getXMLAnswer call in the UI Script is run in a separate thread to the Client Script...

And so, the next line in the Client Script fires before the UI Script is done running:

 

JeremiP_0-1705597080835.png

I can't figure out if/how I could make a Promise call from the Client Script to a UI Script function. I also would prefer not to block the UI until I receive the GlideAjax return, so I don't want to switch away from .getXMLAnswer to something like getXMLWait.

It also seems that the UI script is ran in a different JS context - the g_form API is not available there.

 

What solutions can you think of? Perhaps I can approach this from an entirely another angle?

1 REPLY 1

Sandeep Rajput
Tera Patron
Tera Patron

@JeremiP Please refer to this example on GitHub https://gist.github.com/nfreeze/a9803d7f6049aa814d08de31eb80ee95 where the promise has been added into the UI Script function call itself.

 

Hope this helps.