What is GlideAjax?

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).

Why GlideAjax?

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.

GlideAjax Workflow

  1. Client Script: Creates GlideAjax object and calls server function
  2. Server Script Include: Processes request and returns data
  3. Callback Function: Receives response and updates form

Creating the Script Include

First, create a Script Include that extends AbstractAjaxProcessor with client-callable functions.

Server-Side Script Include
// 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'
});
Script Include Requirements

Must extend AbstractAjaxProcessor. Must be marked as "Client callable". Always return strings from functions. Use this.getParameter() to retrieve parameters.

Client-Side GlideAjax Call

Basic GlideAjax Pattern
// 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);
        }
    }
}

Complete Example: User Lookup

onChange Script with GlideAjax
// 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);
            }
        }
    }
}

Synchronous vs Asynchronous

Asynchronous (Recommended)
// 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 (Avoid if Possible)
// 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);
Best Practice

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.

Common GlideAjax Patterns

Pattern 1: Validate Field Value
// 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');
        }
    });
}
Pattern 2: Populate Dependent Fields
// 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);
        }
    });
}
Pattern 3: Dynamic Choice List
// 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);
            }
        }
    });
}

Error Handling

Robust Error Handling
// 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 '';
    }
}

CSA & CAD Exam Questions (14 Questions)

What does GlideAjax allow you to do? Q1
CSA
  • A) Query the database from client scripts
  • B) Call server-side Script Includes from client scripts
  • C) Create Ajax web services
  • D) Run business rules asynchronously
Show Answer
Correct Answer: B
GlideAjax enables client-side scripts to call server-side Script Includes asynchronously. This allows client scripts to access server resources (like database queries) without directly querying from the browser.
What must a Script Include extend to be callable via GlideAjax? Q2
CSA
  • A) GlideAjax
  • B) AbstractAjaxProcessor
  • C) AjaxHandler
  • D) ClientCallable
Show Answer
Correct Answer: B
Script Includes must extend AbstractAjaxProcessor to be callable from GlideAjax. The Script Include must also be marked as "Client callable".
How do you specify which function to call in a Script Include? Q3
CSA
  • A) ga.setFunction('functionName')
  • B) ga.addParam('sysparam_name', 'functionName')
  • C) ga.callFunction('functionName')
  • D) ga.execute('functionName')
Show Answer
Correct Answer: B
Use ga.addParam('sysparam_name', 'functionName') to specify which function in the Script Include to call. This is a required parameter.
What method initiates an asynchronous GlideAjax call? Q4
CSA
  • A) ga.execute(callback)
  • B) ga.getXML(callback)
  • C) ga.call(callback)
  • D) ga.query(callback)
Show Answer
Correct Answer: B
ga.getXML(callback) initiates an asynchronous call. The callback function receives the response when the server returns data.
How do you retrieve parameters in a server-side Script Include function? Q5
CSA
  • A) gs.getParameter('param_name')
  • B) this.getParameter('sysparam_param_name')
  • C) request.getParameter('param_name')
  • D) current.getValue('param_name')
Show Answer
Correct Answer: B
Use this.getParameter('sysparam_param_name') in the Script Include to retrieve parameters. Note the 'sysparam_' prefix on the parameter name.
What data type must Script Include functions return when called via GlideAjax? Q6
CAD
  • A) Any data type
  • B) String only
  • C) JSON object
  • D) Integer or String
Show Answer
Correct Answer: B
Script Include functions must always return a STRING. For complex data, use JSON.stringify() on the server and JSON.parse() on the client.
How do you extract the answer from a GlideAjax response? Q7
CSA
  • A) response.answer
  • B) response.getAnswer()
  • C) response.responseXML.documentElement.getAttribute('answer')
  • D) response.data
Show Answer
Correct Answer: C
Use response.responseXML.documentElement.getAttribute('answer') to extract the returned value in the callback function.
What's the disadvantage of using getXMLWait() for synchronous calls? Q8
CAD
  • A) It's slower than asynchronous
  • B) It blocks the UI and freezes the browser
  • C) It can't return data
  • D) It requires admin privileges
Show Answer
Correct Answer: B
getXMLWait() blocks the UI, freezing the browser until the server responds. This creates a poor user experience. Always prefer asynchronous getXML() with callbacks.
Must the Script Include checkbox "Client callable" be checked? Q9
CSA
  • A) No, it's optional
  • B) Yes, or GlideAjax calls will fail
  • C) Only for admin users
  • D) Only for asynchronous calls
Show Answer
Correct Answer: B
The "Client callable" checkbox MUST be checked for the Script Include to be accessible via GlideAjax. This is a security feature.
How do you pass multiple parameters to a Script Include function? Q10
CSA
  • A) Use multiple ga.addParam() calls
  • B) Pass an array to addParam()
  • C) Use commas in addParam()
  • D) Only one parameter is allowed
Show Answer
Correct Answer: A
Call ga.addParam() multiple times for multiple parameters. Example: ga.addParam('sysparam_user', userID); ga.addParam('sysparam_group', groupID);
Can you use GlideRecord in a client-callable Script Include? Q11
CAD
  • A) No, it's not allowed
  • B) Yes, Script Includes run server-side
  • C) Only with special permissions
  • D) Only for read operations
Show Answer
Correct Answer: B
Yes! Script Includes run on the server, so you have full access to GlideRecord, gs, and all server-side APIs. This is the primary reason to use GlideAjax.
When should you use synchronous GlideAjax calls? Q12
CAD
  • A) Always, they're more reliable
  • B) Never, always use asynchronous
  • C) Only in onSubmit when you must block submission
  • D) Only for admin users
Show Answer
Correct Answer: C
Synchronous calls should be avoided due to poor UX. Use them ONLY in onSubmit scripts when you must validate data before allowing form submission and need to block the submit action.
What happens if a Script Include function doesn't return a value? Q13
CAD
  • A) An error is thrown
  • B) The callback receives an empty string
  • C) The call times out
  • D) The form refreshes
Show Answer
Correct Answer: B
If no value is returned (or return ''; is used), the callback receives an empty string. Always check for empty responses in your callback function.
What's the correct pattern for returning complex data from a Script Include? Q14
CAD
  • A) Return the object directly
  • B) Use JSON.stringify() on server, JSON.parse() on client
  • C) Use XML formatting
  • D) Return as comma-separated values
Show Answer
Correct Answer: B
For complex data, use JSON.stringify() on the server to convert objects to strings, then JSON.parse() on the client to convert back to objects. This is the standard pattern.

GlideAjax Quick Steps

  1. Create Script Include
  2. Extend AbstractAjaxProcessor
  3. Check "Client callable"
  4. Create function, return string
  5. Client: new GlideAjax('Name')
  6. addParam('sysparam_name', 'func')
  7. Add other parameters
  8. getXML(callback)
  9. Extract answer in callback