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
GlideRecordcall 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:
GlideAjaxand/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.
- Check System Logs: Navigate to System Logs > System Log > All. Filter by Source:
- Incorrect `getParameter()`: Ensure you’re using the exact same parameter names in
getParameter()on the server as you used inaddParam()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
GlideRecordobject 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.
- Use
- `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
onLoadscript 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
incidenttable.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
incidenttable.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
problemtable.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, thevalueshould 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!