Multiple AJAX Values

GlideAJAX comes up in most client side use cases. Many times all you need is a simple value, but in more advanced cases you’ll need a collection of data returned for client processing, especially if it helps reduce the number of round trips to the server.

In this example, I’ll show you two different ways of returning data from a client-callable AJAX script include. (multiple nodes of name value pairs, and a node with multiple attributes) 

Stay tuned for a followup post on how to understand the data being passed and troubleshoot the process.

Let’s start with a simple use case. Since I view this type of data daily, we’ll go with some aviation weather information.
Let’s say I need a client script that retrieves a weather report from my ServiceNow instance. One of the most important weather data sets for a pilots is a METAR, a quick status of the current conditions for an airport.
So our process will take a given airport identifier and return the current METAR data as components. To keep the example simple and easy to follow our script include will just have hard coded strings to return, but I’m sure you’ll get the concept.


First we’ll need a script include for the client-callable service as well as the server-side processing. All GlideAJAX script include classes must extend AbstractAjaxProcessor and have client callable option set to true.
See comments inline.

Script Include
Name: AjaxDemo 
Client Callable: true

/*
 * AjaxDemo script include Description - sample AJAX processor returning multiple value pairs
 */
var AjaxDemo = Class.create();
AjaxDemo.prototype = Object.extendsObject(AbstractAjaxProcessor, {
    
    /*
    * getMETAR method available to client scripts call using: var gajax =
    * new GlideAjax("AjaxDemo"); gajax.addParam("sysparm_name",
    * "getMETAR"); gajax.addParam("sysparm_airport", "KHFD");
    * gajax.getXML(callbackMethod);
    */
    ajaxFunction_getMETAR : function() {
        
        
        // get parameter from client request
        // this example doesn't do much with it but shows how to grab a
        // value from the client
        var airport = this.getParameter("sysparm_airport");
        
        // build new response xml element for 'result' we can call this whatever we want
        // as long as the client knows how to find it newItem is a method from inherited
        // AbstractAjaxProcess, it creates a new xml element
        var result = this.newItem("result");
        
        // add some attributes to the result element
        result.setAttribute("requested_airport", airport);
        result.setAttribute("time", new GlideDateTime());
        
        // add some additional data
        // OPTION 1: add info metar attributes as individual nodes
        // use a simple method (see below) to make our code cleaner
        this._addInfo("metar", "wind", "27007KT");
        this._addInfo("metar", "visibility", "10SM");
        this._addInfo("metar", "clouds", "SCT025 BKN055");
        this._addInfo("metar", "temp", "14");
        this._addInfo("metar", "dewpoint", "02");
        this._addInfo("metar", "altimeter", "2994");
        
        
        // OPTION 2: add metar attributes to a single node
        // this is a better solution if you have multiple airports to return
        // each with their own node and attributes
        var airportInfo = this.newItem("airport_info");
        airportInfo.setAttribute("airport", airport);
        airportInfo.setAttribute("wind", "27007KT");
        airportInfo.setAttribute("visibility", "10SM");
        airportInfo.setAttribute("clouds", "SCT025 BKN055");
        airportInfo.setAttribute("temp", "14");
        airportInfo.setAttribute("dewpoint", "02");
        airportInfo.setAttribute("altimeter", "2994");
        
        // all elements are returned to the client through the inherited
        // methods of AbstractAjaxProcessor since we added them using 
        // the newItem method
    },
    
    /*
    * helper method to create a new node with a custom attribute and value
    */
    _addInfo : function(node, name, value) {
        // create new response node and add the attribute
        var data = this.newItem(node);
        data.setAttribute("name", name); 
        data.setAttribute("value", value);
    },
    
    //in case anyone ever asks
    type: "AjaxDemo" 
});

Now we need to build a client script to make the server request and process the response data. Since we’re just playing, the easiest way to execute a client script is to use the Javascript Executor window [Shift-Control-Alt-J]. You can also just type javascript_executor.do in the navigation filter if you don’t actually need any form data or controls.

Paste the following script in the javascript executor input and click “Run my code”.

//new GlideAjax object referencing the name of our AJAX script include 
var gajax = new GlideAjax("AjaxDemo"); 

// add name parameter to define which method we want to call 
// method name in script include will be ajaxFunction_getMETAR
gajax.addParam("sysparm_name", "getMETAR"); 
// add a parameter 
gajax.addParam("sysparm_airport", "KBDL");
 
// submit request to server
// use ajaxResponse function as callback to process the response
gajax.getXML(ajaxResponse); 
 

function ajaxResponse(serverResponse) { 
// get result element and attributes 
var result = serverResponse.responseXML.getElementsByTagName("result"); 
// we already know we only have one element so we can just use array position [0]
var airport = result[0].getAttribute("requested_airport"); 
var time = result[0].getAttribute("time"); 
 
// build output to display on client for testing 
alert("result attribute data -- \nAirport:" + airport + ", Time: " + time);
 
// get OPTION1 data, each METAR component was sent as a new metar element
// so enumerate through each one
var metar = serverResponse.responseXML.getElementsByTagName("metar");
var option1Output = "OPTION1 Data\n";
for ( var i = 0; i < metar.length; i++) { 
var name = metar[i].getAttribute("name"); 
var value = metar[i].getAttribute("value"); 
option1Output += name + " = " + value + "\n"; 
} 
alert(option1Output);


// get OPTION2 data, all metar components exist in a single airport_info node
// get the first airport_info node and get the values for the attributes specified
var option2Output = "OPTION2 Data";
var airportInfo = serverResponse.responseXML.getElementsByTagName("airport_info")[0];
option2Output += "Airport: " + airportInfo.getAttribute("airport") + "\n";
option2Output += "Altimeter: " + airportInfo.getAttribute("altimeter") + "\n";
option2Output += "Clouds: " + airportInfo.getAttribute("clouds") + "\n";
option2Output += "Dew point: " + airportInfo.getAttribute("dewpoint") + "\n";
option2Output += "Temp: " + airportInfo.getAttribute("temp") + "\n";
option2Output += "Visibility: " + airportInfo.getAttribute("visibility") + "\n";
option2Output += "Wind: " + airportInfo.getAttribute("wind") + "\n";

alert(option2Output); 
}  

If you’re still with me you probably noticed that in option1 we didn’t care what the attribute names were and in option2 we had to specify the attribute name to get the values. Let’s take a look at why that is by viewing the actual payload returned from the server.
Remember, there’s no right or wrong here, just two different options for sending and parsing data.

Here’s the full payload returned from our script include.

<?xml version="1.0" encoding="UTF-8"?>
<xml sysparm_max="15" sysparm_name="getMETAR"
sysparm_processor="AjaxDemo"]]>
<result requested_airport="KBDL" time="2014-11-13 21:07:57" />
<metar name="wind" value="27007KT" />
<metar name="visibility" value="10SM" />
<metar name="clouds" value="SCT025 BKN055" />
<metar name="temp" value="14" />
<metar name="dewpoint" value="02" />
<metar name="altimeter" value="2994" />
<airport_info airport="KBDL" altimeter="2994" clouds="SCT025 BKN055"
dewpoint="02" temp="14" visibility="10SM" wind="27007KT" />
</xml]]>


Let’s break it down so you can see how it was built.

GlideAjax parameters, including the method we called

<xml sysparm_max="15" sysparm_name="getMETAR"
sysparm_processor="AjaxDemo"]]>

Result element created from  var result = this.newItem("result”) and setAttribute calls

<result requested_airport="KBDL" time="2014-11-13 21:07:57" />

Individual metar elements with name value pairs as set by this._addInfo("metar", "wind", "27007KT”). The _addInfo method we used simply set two common attributes (name, value) for each element.

<metar name="wind" value="27007KT" />
<metar name="visibility" value="10SM" />
<metar name="clouds" value="SCT025 BKN055" />
<metar name="temp" value="14" />
<metar name="dewpoint" value="02" />
<metar name="altimeter" value="2994" />

Similar to how we generated the result element, here’s the airport_info element with custom attribute names and values as created by var airportInfo = this.newItem("airport_info”). You can see that this is easier to read and slightly lighter-weight than the option1 structure, especially if we had a lot of airports.

<airport_info airport="KBDL" altimeter="2994" clouds="SCT025 BKN055"
dewpoint="02" temp="14" visibility="10SM" wind="27007KT" />

In the post we’ll review where I found the raw data and how we can use browser developer tools to help us debug GlideAjax transactions.

I recently had a comment on a previous ServiceNow community blog about GlideAJAX. It turns out there’s a script typo that was the result of the migration to Jive. For some reason neither I or the community admins can’t edit the blog or add comments. So I’m posting a corrected, and updated example here to get my new blog kicked off.