The Now Platform® Washington DC release is live. Watch now!

Help
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
The SN Nerd
Mega Sage
Mega Sage

How to Write Smart GlideAjax Quickly

Contents

Part 1 - The Approach

Part 2 - Reusability

Part 3 - Extending Functionality

Part 4 - Implementing GlideAjax with 1 LOC

 

Reusability

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!

 

  1. Free The Shackles: Make your GlideAjax Table Agnostic
  2. The Problem With getReference()
    • Too Much Information
    • Round Trip Problem
  3. Don't Forget the Client

 

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:

  • Table Name
  • Field Name

 

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!

6 Comments