The Now Platform® Washington DC release is live. Watch now!
You've now been given a procedure for writing GlideAjax that hopefully makes it a much more enjoyable process. You're happy that you've learned something new and that you've been able to implement a requirement using Best Practice. That is until your boss gives you a new requirement. He wants the Callers job title shown on the screen as well. Never fear - we have already written the foundations to make this a whole lot easier!
Free The Shackles: Make your GlideAjax Table Agnostic
Let's familiarise ourselves with our previous code example from How to Write Smart GlideAjax Quickly
var UserAjaxUtil = Class.create();
UserAjaxUtil.prototype = Object.extendsObject(AbstractAjaxProcessor, {
ajaxClientDataHandler: function() {
//Get data from the form
var gformData1 = this.getParameter('sysparm_parm1');
//Setup data to return to form
var answer={};
//Do server side stuff
answer['location'] = this.doServerSideStuff(gformData1);
//Encode data to send back to the form
return new JSON().encode(answer);
},
doServerSideStuff: function(userUID) {
var grUser = new GlideRecordSecure('sys_user');
if (grUser.get(userUID)) {
return grUser.getValue('location');
}
},
type: 'UserAjaxUtil'
});
Make a copy of this Script Include and call it 'ShackleFreeAjax'
To make our Script Include Table Agnostic, we will need two additional parameters:
Let's create a new function called 'getPairValueDisplay'.
Our function will now need to take these as parameters, as well as a sys_id.
We will also return the display value.
getPairValueDisplay: function(tableName, sys_id, fieldName) {
var gr = new GlideRecordSecure(tableName);
if (gr.get(sys_id)) {
return { //We want to get the display value too
value: gr.getValue(field),
displayValue: gr.getDisplayValue(fieldName)
} ;
}
},
In Part 1, we recommended splitting your client handler and server-side logic into functions, so you can test them separately.
Make sure you test your server side function in isolation before proceeding.
So, why are we returning the display value?
You may have already noticed from our previous example that the display value always auto-populates, even if we are just returning the sys_id.
Isn't it just adding needless complexity?
To answer this, we must first address some of the problems with g_form.getReference()
The problem with getReference()
Too Much Information!
Let's take a look at the classic example from the ServiceNow Wiki - soon to be from the archives (The ServiceNow wiki is being archived on December 10). Every developer has probably seen and/or implemented this example at some stage in their career. The classic Alert if User is VIP example. While I do detest the use of alert, this example serves our purpose.
function onChange(control, oldValue, newValue, isLoading) {
var caller = g_form.getReference('caller_id', doAlert); // doAlert is our callback function
}
function doAlert(caller) { //reference is passed into callback as first arguments
if (caller.vip == 'true')
alert('Caller is a VIP!');
}
http://wiki.servicenow.com/index.php?title=GlideForm_(g_form)#getReference
Now, let's take a look at what ServiceNow returns to the client.
<?xml version="1.0" encoding="UTF-8"?>
<xml sysparm_chars="sys_id=6816f79cc0a8016401c5a33be04be441" sysparm_max="15" sysparm_name="sys_user" sysparm_processor="AJAXGlideRecord" sysparm_type="query">
<item sys_id="6816f79cc0a8016401c5a33be04be441">
<active>true</active>
<building />
<business_criticality>3</business_criticality>
<calendar_integration>1</calendar_integration>
<city />
<company />
<cost_center />
<country />
<date_format />
<department>a581ab703710200044e0bfc8bcbe5de8</department>
<edu_status />
<email>admin@example.com</email>
<employee_number />
<failed_attempts />
<first_name>System</first_name>
<gender />
<home_phone />
<internal_integration_user>false</internal_integration_user>
<introduction />
<last_login />
<last_login_device>125.209.179.222</last_login_device>
<last_login_time>2017-11-29 12:33:06</last_login_time>
<last_name>Administrator</last_name>
<last_password />
<location>f69521b437d0200044e0bfc8bcbe5d6e</location>
<manager />
<middle_name />
<mobile_phone />
<name>System Administrator</name>
<notification>2</notification>
<phone />
<photo />
<preferred_language />
<schedule />
<source />
<state />
<street />
<sys_class_name>sys_user</sys_class_name>
<sys_created_by>fred.luddy</sys_created_by>
<sys_created_on>2007-07-03 18:48:47</sys_created_on>
<sys_domain>global</sys_domain>
<sys_domain_path>/</sys_domain_path>
<sys_id>6816f79cc0a8016401c5a33be04be441</sys_id>
<sys_mod_count>74</sys_mod_count>
<sys_updated_by>admin</sys_updated_by>
<sys_updated_on>2017-11-28 11:55:50</sys_updated_on>
<time_format />
<time_zone />
<title>System Administrator</title>
<user_name>admin</user_name>
<vip>false</vip>
<web_service_access_only>false</web_service_access_only>
<zip />
</item>
</xml>
Whoah!
All I wanted to know was if the user was a VIP.
Instead, things got a little personal and were provided with all the user data that ACL's allows me to see.
Too much information!
You'll also notice that there is no display values returned!
How does ServiceNow magically know what the display value is?
Round Trip Problem
When your web browser receives the payload shown above, it doesn't know the display values.
So what actually happens when you try and populate a Reference field with only a sys_id?
21:36:08 (094)incident.do[00:00:00.644] *** WARNING *** GlideAjax.getXMLWait - synchronous function - processor: AjaxClientHelper
The calling card of the Round Trip problem.
You've probably seen this flying around your logs.
Chances are, you are seeing a lot of them.
In fact, you would have been seeing them in the first example too.
You might be thinking you are doing the right thing! You're not using getXMLWait, you're using getXML with a callback!
What is going on?
ServiceNow is automatically going back to the server to get the display value for you with a getXMLWait call thrown in.
Let's take a look at the API reference for g_form.setValue()
https://developer.servicenow.com/app.do#!/api_doc?v=jakarta&id=r_GlideFormSetValue_String_String
It is right there in the documentation! (Yes, it is ironic that the OOTB Client Script BP - Set Caller doesn't set the display value)
To improve performance by preventing a round trip, include a display value in addition to the value, use setValue(fieldName, value, displayValue).
Good thing we are getting the display value now!
These two problems in isolation aren't a big deal. The VIP getReference example is even considered certifiable code for the App Store (as long as the callback is present!)
As more and more client scripts get added to the system, it will slowly start impacting on the end user experience. Too much information, paired with multiple round trips - your users will end up feeling the pain from the coding decisions you made.
Don't Forget the Client
We can't forget about the Client Script.
Let's update our onChange function to use the new Script Include
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
var ga = new GlideAjax('ShackleFreeAjax'); //Name of the Ajax Script Inclide
ga.addParam('sysparm_name','ajaxClientDataHandler'); //Method to call
//Add new parameters for our new GlideAjax Class
ga.addParam('sysparm_tablename','sys_user'); //Table name
ga.addParam('sysparm_sysid',newValue); //newValue
ga.addParam('sysparm_fieldname','location'); //Field name we want to retrieve
ga.getXML(userCallback);
}
Modify our setLocation function to set the display value.
function setLocation(caller) { //returns only the values we need
if (caller) {
g_form.setValue(
'location',
caller.location.value, // use value
caller.location.displayValue //set value to avoid round-trip
);
}
}
Notice there is now no Round Trip!
Full code below.
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
var ga = new GlideAjax('ShackleFreeAjax'); //Name of the Ajax Script Inclide
ga.addParam('sysparm_name','ajaxClientDataHandler'); //Method to call
//Add new parameters for our new GlideAjax Class
ga.addParam('sysparm_tablename','sys_user'); //Table name
ga.addParam('sysparm_sysid',newValue); //newValue
ga.addParam('sysparm_fieldname','location'); //Field name we want to retrieve
ga.getXML(userCallback);}
function userCallback(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
answer = answer.evalJSON();
setLocation(answer);
}
function setLocation(caller) { //returns only the values we need
if (caller) {
g_form.setValue(
'location',
caller.location.value, // use value
caller.location.displayValue //set value to avoid round-trip
);
}
}
And the Script Include, in full:
var ShackleFreeAjax = Class.create();
ShackleFreeAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
ajaxClientDataHandler: function() {
//Get data from the form
var tableName = this.getParameter('sysparm_tablename');
var sysId = this.getParameter('sysparm_sysid');
var fieldName = this.getParameter('sysparm_fieldname');
//Setup data to return to form
var answer={};
//Do server side stuff
answer[fieldName] = this.getPairValueDisplay(tableName, sysId, fieldName);
//Encode data to send back to the form
return new JSON().encode(answer);
},
getPairValueDisplay: function(table, sysId, fieldName) {
var gr = new GlideRecordSecure(table);
if (gr.get(sysId)) {
return {
value: gr.getValue(fieldName),
displayValue: gr.getDisplayValue(fieldName),
toString: function() {return gr.getValue(fieldName);}
} ;
}
},
type: 'ShackleFreeAjax'
});
Next time...
Why Stop At One - Handle for Multiple Fields
GlideAjax in One Line of Code!
Please like/endorse/share if you found this helpful and keep this community great!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.