GlideAjax enables client-side scripts to call server-side Script Includes asynchronously. It's the bridge between client scripts (which can't access the database) and server-side code (which can).
Client scripts run in the browser and cannot directly query the database. GlideAjax allows client scripts to request data from the server without refreshing the page.
First, create a Script Include that extends AbstractAjaxProcessor with client-callable functions.
// Script Include Name: MyAjaxUtils
// Client callable: true
var MyAjaxUtils = Class.create();
MyAjaxUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
// Get user details by sys_id
getUserDetails: function() {
var userID = this.getParameter('sysparam_user_id');
var gr = new GlideRecord('sys_user');
if (gr.get(userID)) {
var result = {
name: gr.name.toString(),
email: gr.email.toString(),
phone: gr.phone.toString(),
department: gr.department.getDisplayValue()
};
return JSON.stringify(result);
}
return '';
},
// Check if user is in group
isUserInGroup: function() {
var userID = this.getParameter('sysparam_user_id');
var groupID = this.getParameter('sysparam_group_id');
var gr = new GlideRecord('sys_user_grmember');
gr.addQuery('user', userID);
gr.addQuery('group', groupID);
gr.query();
return gr.hasNext().toString();
},
// Get active incidents count for user
getActiveIncidents: function() {
var userID = this.getParameter('sysparam_user_id');
var gr = new GlideRecord('incident');
gr.addQuery('assigned_to', userID);
gr.addQuery('active', true);
gr.query();
return gr.getRowCount().toString();
},
type: 'MyAjaxUtils'
});
Must extend AbstractAjaxProcessor. Must be marked as "Client callable". Always return strings from functions. Use this.getParameter() to retrieve parameters.
// Client Script (onChange, onLoad, etc.)
function callAjaxFunction() {
// Step 1: Create GlideAjax object
var ga = new GlideAjax('MyAjaxUtils');
// Step 2: Add function name
ga.addParam('sysparam_name', 'getUserDetails');
// Step 3: Add parameters
ga.addParam('sysparam_user_id', g_form.getValue('assigned_to'));
// Step 4: Call server function with callback
ga.getXML(processResponse);
// Step 5: Process response in callback
function processResponse(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
if (answer) {
var userData = JSON.parse(answer);
g_form.addInfoMessage('User: ' + userData.name + ', Email: ' + userData.email);
}
}
}
// Client Script: onChange - assigned_to field
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || isTemplate) {
return;
}
if (newValue == '') {
return;
}
// Create GlideAjax call
var ga = new GlideAjax('MyAjaxUtils');
ga.addParam('sysparam_name', 'getUserDetails');
ga.addParam('sysparam_user_id', newValue);
ga.getXML(displayUserInfo);
function displayUserInfo(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
if (answer) {
var user = JSON.parse(answer);
// Display user information
var message = 'Contact: ' + user.phone + ' | Email: ' + user.email;
g_form.showFieldMsg('assigned_to', message, 'info');
// Auto-populate related fields
if (user.department) {
g_form.setValue('u_department', user.department);
}
}
}
}
// Asynchronous - doesn't block UI
var ga = new GlideAjax('MyAjaxUtils');
ga.addParam('sysparam_name', 'getActiveIncidents');
ga.addParam('sysparam_user_id', userID);
ga.getXML(callback); // Non-blocking
function callback(response) {
var count = response.responseXML.documentElement.getAttribute('answer');
alert('Active incidents: ' + count);
}
// Synchronous - blocks UI (poor user experience)
var ga = new GlideAjax('MyAjaxUtils');
ga.addParam('sysparam_name', 'getActiveIncidents');
ga.addParam('sysparam_user_id', userID);
ga.getXMLWait(); // BLOCKS - UI freezes
var answer = ga.getAnswer();
alert('Active incidents: ' + answer);
ALWAYS use asynchronous calls (getXML with callback). Synchronous calls (getXMLWait) block the UI and create a poor user experience. Only use synchronous in onSubmit when absolutely necessary.
// Server-side Script Include
validateSerialNumber: function() {
var serialNum = this.getParameter('sysparam_serial');
var gr = new GlideRecord('alm_hardware');
gr.addQuery('serial_number', serialNum);
gr.query();
if (gr.hasNext()) {
return 'exists';
}
return 'valid';
}
// Client-side onChange
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || isTemplate) return;
var ga = new GlideAjax('MyAjaxUtils');
ga.addParam('sysparam_name', 'validateSerialNumber');
ga.addParam('sysparam_serial', newValue);
ga.getXML(function(response) {
var result = response.responseXML.documentElement.getAttribute('answer');
if (result == 'exists') {
g_form.showFieldMsg('serial_number', 'Serial number already exists', 'error');
g_form.clearValue('serial_number');
}
});
}
// Server-side Script Include
getLocationDetails: function() {
var locationID = this.getParameter('sysparam_location_id');
var gr = new GlideRecord('cmn_location');
if (gr.get(locationID)) {
var result = {
city: gr.city.toString(),
state: gr.state.toString(),
country: gr.country.toString(),
time_zone: gr.time_zone.toString()
};
return JSON.stringify(result);
}
return '';
}
// Client-side onChange
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || isTemplate) return;
if (newValue == '') {
g_form.clearValue('city');
g_form.clearValue('state');
return;
}
var ga = new GlideAjax('MyAjaxUtils');
ga.addParam('sysparam_name', 'getLocationDetails');
ga.addParam('sysparam_location_id', newValue);
ga.getXML(function(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
if (answer) {
var loc = JSON.parse(answer);
g_form.setValue('city', loc.city);
g_form.setValue('state', loc.state);
g_form.setValue('country', loc.country);
}
});
}
// Server-side Script Include
getAvailableOptions: function() {
var category = this.getParameter('sysparam_category');
var gr = new GlideRecord('u_options');
gr.addQuery('category', category);
gr.addQuery('active', true);
gr.query();
var options = [];
while (gr.next()) {
options.push({
value: gr.sys_id.toString(),
label: gr.name.toString()
});
}
return JSON.stringify(options);
}
// Client-side onChange
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || isTemplate) return;
// Clear current options
g_form.clearOptions('subcategory');
if (newValue == '') return;
var ga = new GlideAjax('MyAjaxUtils');
ga.addParam('sysparam_name', 'getAvailableOptions');
ga.addParam('sysparam_category', newValue);
ga.getXML(function(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
if (answer) {
var options = JSON.parse(answer);
for (var i = 0; i < options.length; i++) {
g_form.addOption('subcategory', options[i].value, options[i].label);
}
}
});
}
// Client-side with error handling
function callAjaxWithErrorHandling() {
var ga = new GlideAjax('MyAjaxUtils');
ga.addParam('sysparam_name', 'getUserDetails');
ga.addParam('sysparam_user_id', g_form.getValue('assigned_to'));
ga.getXML(handleResponse);
function handleResponse(response) {
try {
var answer = response.responseXML.documentElement.getAttribute('answer');
if (!answer || answer == '') {
g_form.addErrorMessage('No data returned from server');
return;
}
var userData = JSON.parse(answer);
// Process data
g_form.showFieldMsg('assigned_to', 'User: ' + userData.name, 'info');
} catch (e) {
g_form.addErrorMessage('Error processing server response: ' + e.message);
gs.log('GlideAjax error: ' + e.message, 'MyClientScript');
}
}
}
// Server-side with error handling
getUserDetails: function() {
try {
var userID = this.getParameter('sysparam_user_id');
if (!userID) {
return '';
}
var gr = new GlideRecord('sys_user');
if (gr.get(userID)) {
var result = {
name: gr.name.toString(),
email: gr.email.toString() || 'N/A'
};
return JSON.stringify(result);
}
return '';
} catch (e) {
gs.error('getUserDetails error: ' + e.message);
return '';
}
}