Troubleshooting GlideAjax Issues: A Comprehensive Guide






Troubleshooting GlideAjax Issues in ServiceNow


Mastering GlideAjax: Your Definitive Guide to Troubleshooting and Best Practices

As ServiceNow administrators and developers, we’ve all been there. You’ve crafted a seemingly perfect client-side script, expecting it to seamlessly fetch data from the server using GlideAjax, only to be met with… nothing. Or worse, cryptic console errors. GlideAjax is an indispensable tool in our arsenal, enabling dynamic and responsive user experiences by allowing client scripts to communicate with server-side Business Rules or Script Includes. However, like any powerful tool, it comes with its own set of quirks and potential pitfalls.

This article aims to demystify GlideAjax, providing a deep dive into common issues, effective troubleshooting techniques, and best practices. We’ll leverage my experience working across various ServiceNow versions, from Rome all the way up to the latest Washington DC release, to offer practical, real-world insights that will not only help you resolve current problems but also prepare you for those challenging interview questions.

Understanding GlideAjax: The Foundation

At its core, GlideAjax is a client-side JavaScript API that allows you to invoke server-side code asynchronously. This means your form doesn’t freeze while waiting for a response. The primary mechanism involves creating a GlideAjax object, setting parameters, and then calling a server-side script.

A typical GlideAjax call looks something like this:

Client Script (e.g., onChange or onLoad):


var ga = new GlideAjax('MyServerSideScript'); // 'MyServerSideScript' is the name of your Script Include
ga.addParam('sysparm_name', 'getSomeData'); // The function name to execute in the Script Include
ga.addParam('sysparm_user_id', g_user.userID); // Passing a parameter to the server
ga.getXML(ajaxResponse); // Execute the request and provide a callback function

function ajaxResponse(response) {
    var answer = response.responseXML.documentElement.getAttribute('answer');
    if (answer) {
        // Process the answer from the server
        g_form.setValue('some_field', answer);
    }
}
        

Server-Side Script Include (e.g., MyServerSideScript):


var MyServerSideScript = Class.create();
MyServerSideScript.prototype = Object.extendsObject(AbstractAjaxProcessor, {

    getSomeData: function() {
        var userId = this.getParameter('sysparm_user_id');
        // Server-side logic here, e.g., using GlideRecord
        var grUser = new GlideRecord('sys_user');
        if (grUser.get(userId)) {
            return grUser.getValue('department'); // Returning a value to the client
        }
        return ''; // Return empty if user not found
    },

    type: 'MyServerSideScript'
});
        

The `ajaxResponse` function on the client side receives the XML response from the server. The `answer` attribute within the `responseXML` is where your server-side script typically returns its result.

Interview Relevance: GlideAjax Basics

Interviewers often probe your understanding of GlideAjax. Be ready to explain:

  • The difference between synchronous and asynchronous GlideAjax calls (though asynchronous is the standard and recommended).
  • Why you’d use GlideAjax over a direct GlideRecord call in a client script (hint: performance and security).
  • The structure of a Script Include used with GlideAjax (e.g., extending AbstractAjaxProcessor).
  • How to pass parameters and retrieve results.

Common GlideAjax Pitfalls and How to Unravel Them

Despite its elegant design, GlideAjax can be a source of frustration. Here’s a breakdown of the most common issues and how to tackle them.

1. The Elusive `ajaxResponse` Function Not Triggering

This is perhaps the most vexing problem. You set up the `getXML` or `getXMLAnswer` call, but your callback function never executes. What’s happening?

Troubleshooting: `ajaxResponse` Not Triggering

  • Script Include Name Mismatch: Double-check the Script Include name used in new GlideAjax('YourScriptName'). A simple typo can break the entire chain. Ensure it exactly matches the name of your Script Include.
  • Function Name Mismatch: Verify that the `sysparm_name` parameter passed to addParam() accurately reflects the function you intend to call within your Script Include.
  • Server-Side Script Errors: The most frequent culprit. If your server-side script (Business Rule or Script Include) has an unhandled JavaScript error, the entire GlideAjax request can fail silently on the client.
    • Check System Logs: Navigate to System Logs > System Log > All. Filter by Source: GlideAjax and/or User: The user performing the action. Look for any errors occurring around the time of your GlideAjax call.
    • Temporary `gs.log()` Statements: Sprinkle gs.log('Debugging line X'); statements throughout your server-side script to pinpoint where execution might be halting.
  • Incorrect `getParameter()`: Ensure you’re using the exact same parameter names in getParameter() on the server as you used in addParam() on the client. Case sensitivity matters!
  • Script Include Not Active or Accessible: Is the Script Include active? Does the user running the script have the necessary roles to execute it?
  • Incorrect `type` in Script Include: For Script Includes used with GlideAjax, ensure `type: ‘YourScriptName’` is correctly defined at the end of the prototype.

2. Unexpected or Missing Data in the Response

The `ajaxResponse` function fires, but the `answer` is empty, incorrect, or not what you expected. This usually points to issues on the server side.

Troubleshooting: Unexpected/Missing Data

  • `return` Statement Missing or Incorrect: The server-side function must have a `return` statement to send data back to the client. If you’re returning a GlideRecord object directly, that’s not what the client expects. You need to return a value (string, number, boolean).
  • `getValue()` vs. `getDisplayValue()`: On the server, are you returning the correct representation of the data?
    • Use gr.getValue('field_name') to return the actual system ID (sys_id) for reference fields.
    • Use gr.getDisplayValue('field_name') to return the human-readable display value for reference fields.

    The client-side `ajaxResponse` typically expects a simple string or number. If you return a complex object, it might not be processed correctly.

  • `GlideRecord` Logic Errors: Your `GlideRecord` queries on the server might not be returning the expected records due to incorrect conditions or assumptions. Use gs.log() on the server to check the sys_ids of records being fetched.
  • User Permissions: The user running the script might not have permission to read the data you’re trying to retrieve on the server.
  • Client-Side Data Processing: Ensure your client-side `ajaxResponse` function is correctly parsing the received data. For example, if the server returns JSON, you’ll need to parse it using JSON.parse().
  • Using `getXMLAnswer()`: If you are consistently expecting a single string answer, consider using ga.getXMLAnswer(callback), which simplifies the client-side parsing as it directly returns the attribute value.

3. Performance Bottlenecks and Excessive GlideAjax Calls

While GlideAjax is asynchronous, too many rapid-fire calls can still impact user experience and server load. This is particularly common with onChange client scripts.

Troubleshooting: Performance Issues

  • Debouncing/Throttling: If your GlideAjax is triggered by user input (e.g., typing in a field), implement debouncing or throttling to limit the number of requests. Wait for a brief pause in typing before sending the request.
  • Combine Requests: Can multiple pieces of information be fetched in a single server-side call? Instead of three GlideAjax calls to get a user’s department, location, and manager, make one call that returns all three.
  • Use `onLoad` Wisely: Avoid making numerous GlideAjax calls within an onLoad script if possible. Cache data if it’s static or fetch it via a single efficient server-side call.
  • Check Server-Side Efficiency: Ensure your server-side scripts are optimized. Complex queries or loops can still take time.
  • Avoid GlideAjax in Business Rules: GlideAjax is for client-to-server communication. Using it within server-side logic is an anti-pattern and can lead to unexpected behavior and performance issues.

4. Client-Side Scope Issues

With the introduction of scoped applications, managing client and server script interactions requires careful attention to scope.

Troubleshooting: Scope Issues

  • Script Include Scope: Ensure your Script Include is in the correct scope and is marked as “Client Callable” if it needs to be invoked from client scripts.
  • Cross-Scope Invocation: If you need to call a Script Include in a different scope, ensure the target Script Include is explicitly marked as “Client Callable” and the calling scope has been granted access to it (via the application scope configuration).
  • Global Scope Script Includes: When working in scoped applications, be mindful of calling Script Includes in the global scope. While possible, it’s generally better practice to keep logic within its respective scope.

Leveraging ServiceNow’s Core Capabilities for Dynamic Interactions

GlideAjax often complements other ServiceNow features. Understanding how they interrelate can lead to more robust solutions.

GlideRecord in Action: Creating Records Programmatically

While GlideAjax is for client-server communication, understanding server-side scripting with GlideRecord is fundamental. Here are examples of common record creations:

  • Creating a User Account:
    
    var userGr = new GlideRecord('sys_user');
    userGr.initialize();
    userGr.username = 'jdoe';
    userGr.firstname = 'John';
    userGr.lastName = 'Doe';
    userGr.email = 'jdoe@example.com';
    userGr.insert();
                    

  • Creating a Group:
    
    var newGr = new GlideRecord('sys_user_group');
    newGr.initialize();
    newGr.name = 'testing';
    newGr.manager = '62826bf03710200044e0bfc8bcbe5df1'; // Sys ID of the manager
    newGr.email = 'testing@tcs.com';
    newGr.description = 'test';
    newGr.insert();
                    

  • Adding Permissions (Roles):

    For Users:

    
    var userRole = new GlideRecord('sys_user_has_role');
    userRole.setValue('user', '62826bf03710200044e0bfc8bcbe5df1'); // User sys_id
    userRole.setValue('role', '2831a114c611228501d4ea6c309d626d'); // Role sys_id
    userRole.insert();
                    

    For Groups:

    
    var grpRole = new GlideRecord('sys_group_has_role');
    grpRole.setValue('group', '477a05d153013010b846ddeeff7b1225'); // Group sys_id
    grpRole.setValue('role', '2831a114c611228501d4ea6c309d626d'); // Role sys_id
    grpRole.insert();
                    

  • Adding/Removing Group Members:

    Adding:

    
    var grMem = new GlideRecord('sys_user_grmember'); // Note: sys_user_grmember table
    grMem.user = '62826bf03710200044e0bfc8bcbe5df1'; // User sys_id
    grMem.group = '477a05d153013010b846ddeeff7b1225'; // Group sys_id
    grMem.insert();
                    

    Removing:

    
    var grMem = new GlideRecord('sys_user_grmember');
    grMem.addQuery('user', '62826bf03710200044e0bfc8bcbe5df1'); // User sys_id
    grMem.addQuery('group', '477a05d153013010b846ddeeff7b1225'); // Group sys_id
    grMem.query();
    if (grMem.next()) {
        grMem.deleteRecord();
    }
                    

Getting User Information (Client & Server)

Contextual information about the logged-in user is frequently needed.

  • Current Logged-in User System ID (Client-side): g_user.userID
  • Current Logged-in User System ID (Server-side): gs.getUserID()
  • Checking Group Membership (Server-side): gs.getUser().isMemberOf('group name'); (Returns true/false)

Core ITSM Record Creation Examples

These `GlideRecord` examples are crucial for understanding how records are created programmatically on the server, often the target of GlideAjax requests.

  • Creating an Incident:
    
    var gr = new GlideRecord('incident');
    gr.initialize();
    gr.caller_id = '86826bf03710200044e0bfc8be5d94'; // User sys_id
    gr.category = 'inquiry';
    gr.subcategory = 'antivirus';
    gr.cmdb_ci = 'affd3c8437201000deeabfc8bcbe5dc3'; // CI sys_id
    gr.short_description = 'Test record using script';
    gr.description = 'Test record using script';
    gr.assignment_group = 'a715cd759f2002002920bde8132e7018'; // Group sys_id
    gr.insert();
                    

  • Creating a Problem:
    
    var gr = new GlideRecord('problem');
    gr.initialize();
    gr.caller_id = '86826bf03710200044e0bfc8be5d94';
    gr.category = 'inquiry';
    gr.subcategory = 'antivirus';
    gr.cmdb_ci = 'affd3c8437201000deeabfc8be5dc3';
    gr.short_description = 'Test record using script';
    gr.description = 'Test record using script';
    gr.assignment_group = 'a715cd759f2002002920bde8132e7018';
    gr.insert();
                    

  • Creating a Change Request:
    
    var gr = new GlideRecord('change_request');
    gr.initialize();
    gr.category = 'inquiry';
    gr.subcategory = 'antivirus';
    gr.cmdb_ci = 'affd3c8437201000deeabfc8be5dc3';
    gr.short_description = 'Test record using script';
    gr.description = 'Test record using script';
    gr.assignment_group = 'a715cd759f2002002920bde8132e7018';
    gr.insert();
                    

Business Rules: Automating Workflows

Business Rules are the backbone of server-side automation in ServiceNow. They can be triggered by various events and execute scripts to modify data, send notifications, or interact with other parts of the platform.

  • Closing Child Incidents When Parent is Closed:

    Type: After Update Business Rule on the incident table.

    Condition: current.state.changesTo(7); (Assuming state 7 is ‘Closed’)

    
    if (current.state == 7 && current.parent == '') {
        var grChild = new GlideRecord('incident');
        grChild.addQuery('parent', current.sys_id);
        grChild.query();
        while (grChild.next()) {
            grChild.state = 7; // Set the state to Closed
            grChild.update(); // Update the child incident
        }
    }
                    

  • Preventing Incident Closure if Tasks are Open:

    Type: Before Update Business Rule on the incident table.

    Condition: current.state.changesTo(7)

    
    var grTask = new GlideRecord('incident_task');
    grTask.addQuery('incident', current.sys_id);
    grTask.addQuery('state', '!=', 3); // Assuming 3 is the state value for 'Closed'
    grTask.query();
    if (grTask.hasNext()) {
        gs.addErrorMessage('Cannot close the incident because there are open tasks.');
        current.setAbortAction(true); // Prevents the incident from being updated/closed
    }
                    

    This logic can be applied similarly to Problem and Change Request management by querying their respective task tables (e.g., problem_task, change_task).

  • Closing Incidents When a Problem is Closed:

    Type: After Update Business Rule on the problem table.

    Condition: current.state.changesTo(7)

    
    if (current.state == 7) {
        var grIncident = new GlideRecord('incident');
        grIncident.addQuery('problem_id', current.sys_id);
        grIncident.addQuery('state', '!=', 7); // Assuming 7 is the state value for 'Closed'
        grIncident.query();
        while (grIncident.next()) {
            grIncident.state = 7; // Set the state to Closed
            grIncident.update(); // Update the incident
        }
    }
                    

Relationship Between Incident, Problem, and Change Management

Understanding these relationships is key to designing effective workflows.

  • Incident: Represents a disruption to normal service operations. Users report incidents when something isn’t working as expected.
  • Problem: A cause of one or more incidents. Problems are investigated to find the root cause and provide a workaround or permanent fix. If the same incident reoccurs frequently, it suggests a problem needs to be identified.
  • Change Request: A formal proposal to alter a configuration item (CI) or IT service. Changes are managed to minimize risk and disruption. If a solution to a problem requires modifying the system, a change request is initiated.

Number Field Creation

Creating unique, sequential identifiers like incident numbers involves configuring Auto-Number fields.

To create a number field (e.g., for a custom table), navigate to the table’s dictionary entry, go to the “Control” tab, and configure the “Auto-number” field. You’ll specify a prefix (e.g., “INC-“) and the number of digits.

Dictionary Properties and UI Configuration

The ServiceNow platform offers extensive control over how fields behave and are presented.

The `current` Object

The current object is a powerful server-side construct representing the record being processed by a Business Rule, Security Rule, or other server-side operations.

  • Setting Field Values:
    • current.setValue('field_name', value);: Sets the field to a specific value. For reference fields, the value should be the sys_id.
    • current.setDisplayValue('field_name', value);: Sets the field using its display value. This is useful for reference fields where you want to set the value based on its readable name, and ServiceNow will resolve the sys_id.

Reference Qualifiers: Refining Your Selections

Reference qualifiers are crucial for ensuring users select the correct related records, making your forms more intuitive and data more accurate.

Types of Reference Qualifiers:

  • Simple Reference Qualifier: Uses basic query conditions (e.g., active=true, state=1) directly in the dictionary entry. It’s static and doesn’t change based on other form values.
  • Dynamic Reference Qualifier: Leverages “Dynamic Filter Options” defined in System Definition > Dynamic Filter Options. These can be more complex and adapt to certain contextual elements, but are still pre-defined.
  • Advanced Reference Qualifier (JavaScript): This is the most flexible type. It uses JavaScript code to define the query dynamically.

    
    // Example: Filter incidents assigned to the current user's assignment group and priority less than 3
    javascript: 'assignment_group=' + current.assignment_group + '^priority<3';
                    

    This allows for complex logic based on multiple form fields.

Difference: Simple is static. Dynamic uses pre-defined options. Advanced uses custom JavaScript to provide the most dynamic filtering.

Dependent Values

Dependent values create cascaded dropdowns. When a user selects a value in one field (the parent), the available options in another field (the dependent) are filtered accordingly. For example, selecting “Hardware” in the Category field might filter the Subcategory field to show only “Laptop,” “Desktop,” etc. This is configured in the dependent field’s dictionary entry by setting the “Dependent field” attribute.

Calculated Values

A “Calculated” dictionary property allows you to define a formula or script that automatically populates a field’s value based on other fields in the current record. This is executed server-side when the record is saved.

Attributes

Attributes are modifiers applied to dictionary entries (fields) to alter their behavior on the form.

  • Common attributes include: no_email (disables email notifications for changes to this field), no_attachment (disables attachments for this field), tree_picker (displays the field as a tree picker).

Dictionary Overrides

Dictionary overrides are used to customize fields in a child table without affecting the parent table. For instance, you can change the default value, make a field mandatory, or alter its display type for a specific table (like Incident) even if it inherits that field from a parent table (like Task). You can override properties like: Display name, Mandatory, Read-only, Default value, Max length, Reference specification, etc.

UI Policies vs. Data Policies

These two features control form behavior, but with different scopes.

  • UI Policies: Client-side. They control the visibility, mandatory status, and read-only state of fields based on conditions. They execute when the form loads or when field values change.

    • Reverse if false: If checked, the UI Policy actions are reversed when the condition becomes false.
    • On Load checkbox: If checked, the UI policy runs when the form initially loads. If unchecked, it only runs in response to field changes.
    • Running Scripts: UI Policies can execute client-side JavaScript by enabling the “Run scripts” checkbox.
  • Data Policies: Can execute on both client and server. They enforce data consistency and make fields mandatory or read-only, regardless of how the data is entered (form, import sets, APIs).

Converting UI Policies to Data Policies

ServiceNow provides a handy “Convert to Data Policy” feature. However, there are cases where this conversion is not directly supported:

  • When controlling data visibility (i.e., hiding/showing fields – Data Policies primarily enforce data constraints, not direct visibility like UI Policies).
  • When controlling views.
  • When controlling related lists (UI Policies can manage related lists; Data Policies focus on data itself).
  • When the UI Policy heavily relies on complex client-side scripting that cannot be directly translated to server-side or declarative rules.

Conclusion

GlideAjax, when understood and implemented correctly, is a powerful ally in creating dynamic and responsive ServiceNow applications. By diligently applying the troubleshooting steps outlined here, paying close attention to server-side logic, and leveraging the full suite of ServiceNow configuration options from dictionary attributes to UI and Data Policies, you can overcome common hurdles. Remember, consistent practice and a methodical approach are key to mastering these tools. This knowledge not only makes you a more effective administrator but also significantly boosts your confidence in technical interviews. Happy scripting!


Scroll to Top