Michael Jones -
Giga Sage

As I've mentioned in a previous article, GlideRecord is perhaps the most powerful tool in a developer's tool-kit and, while undisputed, GlideRecord has one undeniable drawback; it is difficult to use effectively client-side. While there are certain circumstances where you can get away with it you will eventually encounter a situation where you need an alternative.

For instance, say you tried to do this client-side with GlideRecord: 

	var user = new GlideRecord('sys_user');
	user.addQuery('sys_id', newValue);
	user.query(callBack);
	
	function callBack() {
		while(user.next()) {
		alert(user.email);	
		}
		
	}

If you are logged in as an admin or an itil user, this will return a result. If you are logged in with a user with no roles, it will return undefined. This can be particularly frustrating as, let's face it, we develop as an admin and only test as user later, which makes your first encounter with this wall a nasty surprise! 

GlideAjax to the rescue!

With GlideAjax you are able to create a bridge between client-side and server-side operations. Granted, it's something of a toll-bridge, but it's a bridge none-the-less and one you'll undoubtably want to cross at some point. 

At its core GlideAjax consists of a script include, which contains the code that is executed server-side, formatted in a specific way, and a client-script that is used to pass data to the server and process the response. GlideAjax is most commonly used to send a request from the client to the server to execute a GlideRecord query and return the results and is most often used in catalog items (in my experience). 

A simple example (breaks through the wall we ran into at the start) would be:

Script Include:

Name: exampleAJ

Client callable: True

Script: 

var exampleAJ = Class.create();
exampleAJ.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	
	getUserEmail: function() {
	var user_id = this.getParameter('sysparm_user_id');
        var user = new GlideRecord('sys_user');
        user.get(user_id);
        return user.email;	
		
	},
	type: 'exampleAJ'
	
});

And client side: Catalog Item with a Reference Field to sys_user named user and a string field named email.

On Change of a user: 

function onChange(control, oldValue, newValue, isLoading) {
	if (isLoading || newValue == '') {
		return;
	}
	
	var getEmail = new GlideAjax('exampleAJ');
	getEmail.addParam('sysparm_name', 'getUserEmail');
	getEmail.addParam('sysparm_user_id', newValue);
	getEmail.getXML(showEmail);
	
	function showEmail(response) {
		var answer = response.responseXML.documentElement.getAttribute("answer");
		
		//set a field, or produce an alert for testing.
		g_form.setValue('email', answer);
		//alert(answer);
	}
	
}

When you change the user field, the sys_id of the selected user (newValue) is passed to the GlideAjax call, which responds with the email address of the user. This particular example is simple, but illustrates one of the key features of GlideAjax:

The ability to perform a function or operation outside of the scope of the logged in user, and return a value client side. Where the GlideRecord script would only work as an admin or itil user, the GlideAjax example will work regardless of who the logged in user is.   

The real power of GlideAjax is its ability to return results (and perform operations) that are completely outside of the usual scope for the logged-in user. A simple example of this might be a situation where you want to populate additional information about a selected user's manager on a catalog item. Most users (and queries under the context of those users) can access their own data but fail to return extended data about other users, for example an e-mail address or phone number. 

With great power comes great responsibility!

Be aware that anything you can do via GlideAjax has the potential of being done by anyone using a connected and authenticated client. These script includes are client-callable, which means that they could ... potentially ... be used in a manner contrary to your intended design. This is a long-winded way of saying that GlideAjax gives you the ability to bypass certain security constraints in order to accomplish desired functionality but those constraints are there for a reason. While it can be tempting (and often necessary) to make GlideAjax functions that are flexible and configurable, you need to balance that flexibility with caution and common sense.

With all of that in mind you start to realize the power of GlideAjax when you consider the fact that, while returning data is its intended purpose it's not exactly a hard requirement. Your server-side script include has no strict limitations on what operations you can perform!

Anything you can do with GlideRecord you can do with GlideAjax

Let's take an interesting example. Let's say you've created a very complicated Catalog Item with a lot of moving parts and, occasionally, there are errors that occur for reasons that can't always be avoided. When it happens you need some extensive data logging to help you figure out the cause but, with everything being client-side that can be challenging. 

One potential solution would be to write your error handling scripts so that your output is an object and then pass that object to a GlideAjax call and write to the system log. In this instance you're not waiting for or expecting a response, just using GlideAjax as a bridge. 

Script Include:

Name: logProcessor

Client callable: True

Script: 

var logProcessor = Class.create();
logProcessor.prototype = Object.extendsObject(AbstractAjaxProcessor, {
	
	writeLog: function() {
		gs.info(this.getParameter('sysparm_id_str') + '||' + this.getParameter('sysparm_err'));
		var obj = JSON.parse(this.getParameter('sysparm_err'));
		return ''; //You can add a value for testing
	},

    type: 'logProcessor'
});

 And client side:

//you would create an object (as an example) in your error handling routine to hold the data points you need
var obj = {};
obj.user = g_user.userID();
obj.item = 'My Complex Catalog Item';
obj.selected_ci = g_form.getValue('u_cmdb_ci'); //just an example mind you
obj.error = "error returned by your error handling";
///you get the idea

//Then at the end, you call your GlideAjax to send the data server side for logging;

var ga = new GlideAjax('logProcessor');
ga.addParam('sysparm_name', 'writeLog');
ga.addParam('sysparm_id_str', 'ComplexItemError');
ga.addParam('sysparm_err', JSON.stringify(obj));
ga.getXML(ret);

function ret(response) {
    //We don't really need to do anything here, but you could use this for testing
    //var answer = response.responseXML.documentElement.getAttribute("answer");
    //alert(answer);
}

 The result is that you have a server-side log that you can now reference when troubleshooting issues client-side. 

This example is somewhat simple and of limited utility but it illustrates that once you stop thinking of GlideAjax as a simple data retrieval method, it opens up possibilities that might help you address some of the more unique challenges that come up from time to time. 

I'll reiterate here that while GlideAjax is a powerful tool it is one you want to wield with caution. Whenever possible be sure that you are limiting the scope and functionality using the least privileged approach and avoid creating "uber" Ajax calls that accept open-ended parameters such as table names and field names; again, you want to prevent any possibility of malicious usage.

You should also use extreme care to limit updating records via GlideAjax and I would personally never use GlideAjax to delete a record; those are operations you want to keep strictly controlled!

If you found this article useful or helpful, please be kind a click appropriately! If you really found it useful, make bookmark it for later reference 🙂

Version history
Last update:
‎05-30-2020 07:43 PM
Updated by: