Mastering ServiceNow: A Real-World GlideAjax Use Case Example
Hey there, fellow ServiceNow enthusiasts! Today, we’re going to roll up our sleeves and tackle something that’s absolutely crucial for building responsive and dynamic ServiceNow applications: GlideAjax. If you’ve been in the ServiceNow ecosystem for a bit, you’ve likely encountered those moments where you needed to fetch data or perform an action on the server without a full page refresh. That’s where GlideAjax shines, and understanding its nuances can truly elevate your development game and make you a standout candidate in interviews.
The challenge often lies in translating abstract concepts into tangible, working solutions. While many resources explain *what* GlideAjax is, fewer offer a truly in-depth, practical, and human-readable walkthrough of a common, real-world scenario. That’s precisely what we’re aiming for today. We’ll walk through a realistic use case, break down the code step-by-step, discuss best practices, and even touch upon some common pitfalls and how to navigate them. So, grab your favorite beverage, get comfortable, and let’s dive in!
Why GlideAjax? The Power of Asynchronous Communication
Before we jump into our specific use case, let’s quickly revisit *why* GlideAjax is so important. In the world of web development, users expect applications to be fast and interactive. Nobody enjoys a page that freezes while it waits for server-side processing. Traditional synchronous calls would halt the user’s interaction until the server responded.
GlideAjax, on the other hand, leverages asynchronous JavaScript. This means that when you initiate a GlideAjax call, the user’s browser isn’t blocked. They can continue clicking buttons, typing in fields, or navigating other parts of the interface while the request is being sent to the ServiceNow server and processed. Once the server has a response, it can then update the user interface without a disruptive full page reload. This asynchronous behavior is key to creating a smooth, modern user experience within the ServiceNow platform.
Think of it like ordering at a busy restaurant:
- Synchronous: You place your order, then stand at the counter, staring at the chef, completely unable to do anything else until your food is ready.
- Asynchronous (GlideAjax): You place your order, get a buzzer, and then go sit at your table, chat, browse your phone, or even read a book. When your food is ready, the buzzer goes off, and you go pick it up. Much more efficient and pleasant, right?
In ServiceNow, GlideAjax allows our client-side scripts (like UI Policies, Client Scripts, and Catalog Client Scripts) to talk to server-side scripts (Script Includes) to perform tasks like:
- Fetching specific data from other tables.
- Validating data based on complex server-side logic.
- Performing calculations or updates that require server-side access.
- Triggering server-side workflows or integrations.
Our Real-World Use Case: Dynamic Field Population on Incident Form
Let’s imagine a common scenario in ServiceNow’s ITSM module: the Incident form. When a user creates or updates an incident, they often select a Configuration Item (CI). Based on the selected CI, we might want to dynamically populate other fields, such as the Assignment Group and the CI’s Support Email. This makes the incident creation process faster and more accurate, reducing manual entry and potential errors.
Here’s how we’ll achieve this using GlideAjax:
- When a user selects a CI on the Incident form, a client script will trigger.
- This client script will use GlideAjax to send the selected CI’s sys_id to a server-side Script Include.
- The Script Include will query the CMDB (Configuration Management Database) to find relevant information about that CI, specifically its associated Assignment Group and its Support Email.
- The Script Include will then return this information back to the client script.
- Finally, the client script will use the returned data to populate the Assignment Group and CI Support Email fields on the Incident form.
The Building Blocks: Client Script and Script Include
To implement this, we’ll need two main components:
1. The Server-Side Script Include: The Data Provider
This is where the “heavy lifting” happens. Our Script Include will be responsible for receiving the CI sys_id, querying the CMDB, and returning the requested data. Let’s create a Script Include named CIRecordInfoFetcher.
Steps to Create the Script Include:
- Navigate to System Definition > Script Includes.
- Click New.
- Fill in the fields as follows:
- Name:
CIRecordInfoFetcher - API Name: (This will auto-populate to
global.CIRecordInfoFetcherif you’re in the global scope) - Client callable: Check this box. This is CRITICAL for GlideAjax to work. It makes the functions within this Script Include accessible from client-side scripts.
- Script: Paste the following code.
- Name:
var CIRecordInfoFetcher = Class.create();
CIRecordInfoFetcher.prototype = {
initialize: function() {
},
/**
* Fetches Assignment Group and Support Email for a given CI.
* @param {string} ciSysId - The sys_id of the Configuration Item.
* @returns {string} A JSON string containing assignment_group and support_email.
* Returns an empty object '{}' if CI is not found or data is missing.
*/
getCiDetails: function() {
var ciSysId = this.getParameter('sysparm_ci_sys_id'); // Get parameter passed from client
var result = {}; // Object to hold our returned data
if (!ciSysId) {
gs.error("CIRecordInfoFetcher: CI sys_id not provided.");
return JSON.stringify(result); // Return empty object as JSON
}
var ciGr = new GlideRecord('cmdb_ci'); // We'll query the base CI table for simplicity.
// In a real scenario, you might query specific CI classes.
if (ciGr.get(ciSysId)) {
// Fetching the Assignment Group.
// This might be a reference field like 'support_group' or a related list.
// For this example, let's assume a direct reference field named 'support_group'.
// Adjust 'support_group' to match your actual CMDB field name.
if (ciGr.getValue('support_group')) {
result.assignment_group = ciGr.getValue('support_group'); // Store the sys_id of the group
// If you need the display name, you'd do another GlideRecord lookup here or fetch it on the client.
// For now, we'll just return the sys_id.
}
// Fetching the Support Email.
// Again, assume a direct field named 'support_email'.
// Adjust 'support_email' to match your actual CMDB field name.
if (ciGr.getValue('support_email')) {
result.support_email = ciGr.getDisplayValue('support_email'); // Get the email address
} else {
// If there's no direct support email, you might have logic to find it
// from a related record or a default email for the CI class.
// For simplicity, we'll assume it's a direct field.
}
} else {
gs.error("CIRecordInfoFetcher: CI with sys_id '" + ciSysId + "' not found.");
}
gs.info("CIRecordInfoFetcher: Returning result: " + JSON.stringify(result));
return JSON.stringify(result); // Return the data as a JSON string
},
type: 'CIRecordInfoFetcher'
};
Key points about this Script Include:
Class.create(): This is the standard way to define classes in ServiceNow’s server-side JavaScript environment.initialize(): The constructor function, though often empty for simple utility scripts.getCiDetails(): This is the *function* that our client script will call. We can name it anything descriptive.getParameter('sysparm_ci_sys_id'): GlideAjax passes parameters from the client to the server using this method. The parameter name (sysparm_ci_sys_id) is arbitrary but should be consistent between client and server. Conventionally, parameters are prefixed withsysparm_.GlideRecord('cmdb_ci'): We’re querying the basecmdb_citable. In a production environment, you’d likely query more specific CI tables (e.g.,cmdb_ci_service,cmdb_ci_server) depending on your requirements. You would also need to adjust the field names (e.g.,support_group,support_email) to match your CMDB’s schema. These are example fields; your actual CMDB might have different field names for the assignment group and support email.result = {}: We initialize an empty JavaScript object to store the data we retrieve.ciGr.get(ciSysId): This attempts to fetch the record from the CMDB using the provided sys_id.ciGr.getValue('support_group')andciGr.getDisplayValue('support_email'): These methods retrieve the value of a field.getValuetypically returns the sys_id for reference fields, whilegetDisplayValuereturns the human-readable label.JSON.stringify(result): Crucially, GlideAjax can only return strings. We’re converting our JavaScript object into a JSON string to send it back to the client. The client script will then parse this JSON string back into a JavaScript object.gs.error()andgs.info(): These are good for debugging and logging server-side behavior. You can view these messages in the System Logs.client callable: true: This checkbox is absolutely essential. Without it, the client script won’t be able to invoke the Script Include’s functions.
2. The Client Script: The User Interface Interactor
Now, let’s create the client-side logic that will trigger the GlideAjax call and update the form. We’ll create a Client Script on the Incident table.
Steps to Create the Client Script:
- Navigate to System Definition > Client Scripts.
- Click New.
- Fill in the fields as follows:
- Name:
Populate CI Details on Incident - Table:
Incident [incident] - UI Type:
Desktop(orAllif you want it to work on mobile too) - Type:
onChange - Field name:
Configuration item(This is the field that will trigger the script) - Script: Paste the following code.
- Name:
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return; // Exit if the form is loading or the CI field is empty
}
var ciSysId = newValue; // The newValue of the 'Configuration item' field is its sys_id
// Create a new GlideAjax object
var ga = new GlideAjax('CIRecordInfoFetcher'); // Pass the API name of your Script Include
// Add parameters to send to the Script Include
ga.addParam('sysparm_name', 'getCiDetails'); // The name of the function to call in the Script Include
ga.addParam('sysparm_ci_sys_id', ciSysId); // The sys_id of the selected CI
// Execute the GlideAjax call asynchronously
ga.getXMLAnswer(function(answer) {
// This callback function executes when the server responds
// Parse the JSON answer back into a JavaScript object
var result;
try {
result = JSON.parse(answer);
g_form.log("Parsed GlideAjax result: " + JSON.stringify(result));
} catch (e) {
g_form.log("Error parsing GlideAjax answer: " + answer + ". Error: " + e.toString());
return; // Exit if parsing fails
}
// Update form fields based on the returned data
if (result.assignment_group) {
g_form.setValue('assignment_group', result.assignment_group);
g_form.log("Set assignment_group to: " + result.assignment_group);
} else {
// Optionally clear the field if no group is returned
g_form.clearValue('assignment_group');
g_form.log("No assignment_group returned, clearing field.");
}
if (result.support_email) {
g_form.setValue('u_ci_support_email', result.support_email); // Assuming 'u_ci_support_email' is a custom field
g_form.log("Set u_ci_support_email to: " + result.support_email);
} else {
// Optionally clear the field if no email is returned
g_form.clearValue('u_ci_support_email');
g_form.log("No support_email returned, clearing field.");
}
});
}
Key points about this Client Script:
onChange(control, oldValue, newValue, isLoading, isTemplate): This is the standard signature for anonChangeclient script. We’re interested innewValue, which is the sys_id of the selected CI when the field changes.if (isLoading || newValue === ''): This is a good practice to prevent unnecessary GlideAjax calls when the form is just loading or when the CI field is cleared.var ga = new GlideAjax('CIRecordInfoFetcher');: This is the core of GlideAjax. We instantiate a newGlideAjaxobject and pass the API name of the Script Include we want to call (e.g.,global.CIRecordInfoFetcher). If your Script Include is in a scoped application, use its scoped API name.ga.addParam('sysparm_name', 'getCiDetails');: This is how we tell the server-side script *which function* to execute. The parameter namesysparm_nameis a convention, but the function namegetCiDetailsmust match the function in your Script Include.ga.addParam('sysparm_ci_sys_id', ciSysId);: We’re passing the sys_id of the selected CI as a parameter. Again,sysparm_ci_sys_idmust match the parameter name expected by the Script Include’s function (this.getParameter('sysparm_ci_sys_id')).ga.getXMLAnswer(function(answer) { ... });: This is the method that initiates the asynchronous request. It takes a callback function as an argument. This callback function will be executed once the server has processed the request and sent back a response.answerwill contain the string returned by the server-side Script Include.JSON.parse(answer): Inside the callback, we useJSON.parse()to convert the JSON string received from the server back into a usable JavaScript object.- Error Handling (`try…catch`): It’s crucial to wrap your `JSON.parse` in a `try…catch` block. If the server returns malformed JSON or an unexpected string, `JSON.parse` will throw an error, and your script will crash. This `try…catch` gracefully handles such issues.
g_form.setValue('field_name', value);: This is the standard ServiceNow API to set the value of a form field from a client script.g_form.clearValue('field_name');: Use this to clear a field’s value if the server doesn’t return data for it.- `g_form.log()`: Another helpful method for debugging. You can view these logs in the browser’s developer console.
- Field Names: Note that I’ve used
assignment_group(which is standard) andu_ci_support_email. You’ll need to replaceu_ci_support_emailwith the actual name of the field on your Incident form where you want to display the CI’s support email. If this field doesn’t exist, you’ll need to create it first.
Testing Your Implementation
Now for the exciting part – testing!
- Save both your Script Include and your Client Script.
- Navigate to the Incident form and create a new incident.
- Start typing in the Configuration item field and select a CI that you know has an associated Assignment Group and Support Email defined in your CMDB.
- Observe as the Assignment Group and your custom CI Support Email field (e.g.,
u_ci_support_email) should populate automatically without the page refreshing. - If you have browser developer tools open (usually F12), you can check the “Console” tab to see the `g_form.log` messages from your client script and any errors. You can also check the “Network” tab to see the GlideAjax request being made.
Troubleshooting Common GlideAjax Issues
Even with the best intentions, things can sometimes go awry. Here are some common issues and how to tackle them:
1. “Script Include not found” or “function is not defined” errors
- Cause: The API name of the Script Include passed to `new GlideAjax()` is incorrect, or the Script Include is not marked as “Client callable”.
- Solution:
- Double-check the API name in your client script (`new GlideAjax(‘YourScriptIncludeName’)`) against the “API Name” field in the Script Include definition.
- Ensure the “Client callable” checkbox is checked on the Script Include.
- If you’re in a scoped application, ensure you’re using the correct scoped API name.
2. Empty or Incorrect Data Returned
- Cause:
- The parameter name passed from the client doesn’t match the parameter name expected by the server (`getParameter`).
- The `GlideRecord` query in the Script Include is incorrect (wrong table, wrong sys_id, wrong field names).
- The data doesn’t exist in the CMDB for the selected CI.
- The `JSON.stringify()` or `JSON.parse()` operations are failing.
- Solution:
- Check Logs! This is your best friend. View system logs for errors from your Script Include and browser console logs for errors/logs from your Client Script (`g_form.log`).
- Verify that parameter names (`sysparm_ci_sys_id`) match between `addParam()` and `getParameter()`.
- Step through your `GlideRecord` logic in the Script Include. Use `gs.info()` or `gs.debug()` to log values at each step.
- Confirm the CI you’re testing with actually has the data you expect in the CMDB.
- Ensure `JSON.parse()` is in a `try…catch` block. Log the raw `answer` string to see what the server is actually returning.
3. Form Fields Not Updating
- Cause:
- The field names used in `g_form.setValue()` are incorrect.
- The `getXMLAnswer` callback function is not executing, or it’s exiting prematurely due to an error.
- Solution:
- Double-check the field names in `g_form.setValue()` and `g_form.clearValue()`.
- Use `g_form.log()` within the `getXMLAnswer` callback to confirm it’s being reached. If not, an error might be occurring earlier, like during `JSON.parse()`.
- Inspect the `answer` variable in the callback to see what data you are actually receiving.
4. Performance Issues (Slow Response Times)
- Cause: Complex or inefficient server-side queries, too many GlideAjax calls in quick succession.
- Solution:
- Optimize your `GlideRecord` queries in the Script Include. Use `setLimit()` if you only expect one record. Avoid loops if possible.
- Cache frequently accessed data on the server if applicable.
- Consider if the data *really* needs to be fetched every time the CI changes. Maybe it only needs to be fetched on record creation.
- Avoid making multiple GlideAjax calls within a single callback if you can combine them into one server-side call.
Interview Relevance: Why This Matters
Understanding GlideAjax is not just about writing code; it’s about understanding how to build efficient and user-friendly applications within ServiceNow. When you’re in an interview, demonstrating your knowledge of GlideAjax can set you apart significantly.
Interviewers will often ask questions like:
- “Can you explain how you would fetch data from another table without a full page reload?” (This is the classic GlideAjax question.)
- “What’s the difference between synchronous and asynchronous calls in JavaScript/ServiceNow?”
- “When would you use GlideAjax versus a UI Policy or Business Rule?” (Hint: UI Policies are client-side only, Business Rules are server-side only and run automatically, GlideAjax is for on-demand client-to-server communication.)
- “Describe a situation where you used GlideAjax to improve user experience.” (Having a real-world example like the one we discussed is perfect here.)
- “How do you handle errors with GlideAjax?” (This shows you think about robustness.)
- “What are the parameters you need to set for a GlideAjax call?” (
sysparm_nameand other custom parameters.)
By having a solid grasp of the architecture, the code, and the troubleshooting techniques for GlideAjax, you can confidently answer these questions and showcase your practical skills.
Beyond the Basics: Further Enhancements
Our example is a great starting point, but here are some ideas for how you could extend this:
- Handling CI Classes: Instead of querying the base `cmdb_ci` table, you could inspect the CI class (`ciGr.getRecordClassName()`) and query specific tables or apply different logic based on the class.
- Fetching Related Data: If the Assignment Group isn’t a direct field but determined by a relationship, you’d adjust the `GlideRecord` query accordingly.
- More Complex Data: Return multiple fields, related records, or even perform calculations on the server.
- Error Messaging: Display user-friendly error messages on the form if the GlideAjax call fails or returns no data.
- Debouncing/Throttling: For fields that change very rapidly (e.g., typing in a search box), you might want to implement debouncing to avoid making too many GlideAjax calls.
Conclusion
GlideAjax is a fundamental tool in the ServiceNow developer’s arsenal. It empowers us to create dynamic, responsive, and user-friendly applications by enabling seamless communication between the client and server. By understanding the mechanics, practicing with real-world examples like our CI details fetcher, and knowing how to troubleshoot effectively, you’ll not only build better ServiceNow solutions but also significantly boost your confidence in technical discussions and interviews.
Keep experimenting, keep learning, and happy coding!