Mastering ServiceNow Scratchpad: A Deep Dive for Admins and Developers
In the dynamic world of ServiceNow, efficiency and elegant solutions are paramount. As we navigate through various versions, from Rome to the latest Washington D.C. release, understanding the nuances of core functionalities becomes increasingly critical. One such fascinating, yet sometimes overlooked, area is the judicious use of the scratchpad. This article aims to demystify the scratchpad, offering practical insights, real-world examples, and tips relevant for both seasoned professionals and those preparing for interviews.
I’ve had the opportunity to work with ServiceNow across several major releases, including Rome, San Diego, Tokyo, Utah, Vancouver, and now Washington D.C. This journey has provided a unique perspective on how features evolve and best practices solidify. Today, we’ll focus on a specific aspect of server-side scripting and client-side interactions: the power of the scratchpad object.
What Exactly is the ServiceNow Scratchpad?
At its heart, the scratchpad is a mechanism for passing data from the server-side script (like a Business Rule or Script Include) to the client-side script (typically running in UI Policies or Client Scripts). Think of it as a temporary storage bin that a server-side script can fill up with information just before the client-side script gets a chance to run. This is particularly useful when you need to perform client-side actions based on data that’s only available or complex to compute on the server.
The scratchpad object is a JavaScript object that is available on both the server and client. However, its primary utility lies in its ability to hold values set by server-side scripts and then be accessed by client-side scripts. It’s important to note that the scratchpad is unique to each form load and is not persistent. Once the form is submitted or refreshed, the scratchpad is cleared.
Why Use the Scratchpad? The Problem it Solves
Imagine a scenario where you need to dynamically set a field’s value or visibility on a form based on a complex calculation or data lookup that can only be efficiently performed on the server. You could try to achieve this with client-side scripting, but it might involve multiple server calls or inefficient processing. This is where the scratchpad shines.
It allows you to perform these heavy computations on the server and then simply pass the resulting value(s) to the client. The client-side script then reads these values from the scratchpad and acts upon them. This not only simplifies client-side logic but also improves performance by reducing the number of client-server interactions.
Understanding User and Group Management in ServiceNow
Before we dive deeper into scratchpad examples, a quick refresher on user and group management is essential, as these concepts often intertwine with scripting and data handling. Understanding these fundamentals is also key for many ServiceNow interviews.
User and Group Tables
- User Table: The primary table for user information is
sys_user. - Group Table: The table for defining groups is
sys_user_group. - Group Member Table: The table linking users to groups is
sys_user_grmember.
Managing Permissions (Roles)
Permissions are managed through roles. Best practice dictates assigning roles to groups rather than individual users. This simplifies management, especially when employees join or leave the organization. Removing a user from a group automatically revokes their associated roles.
- Adding Roles to Users via Script: This creates a record in the
sys_user_has_roletable.
var userRole = new GlideRecord('sys_user_has_role');
userRole.setValue('user', 'USER_SYS_ID'); // Replace with actual user sys_id
userRole.setValue('role', 'ROLE_SYS_ID'); // Replace with actual role sys_id
userRole.insert();
sys_group_has_role table.
var grpRole = new GlideRecord('sys_group_has_role');
grpRole.setValue('group', 'GROUP_SYS_ID'); // Replace with actual group sys_id
grpRole.setValue('role', 'ROLE_SYS_ID'); // Replace with actual role sys_id
grpRole.insert();
User Delegation
User delegation allows one user to perform actions on behalf of another, typically during absences like vacations. This is configured on the original user’s record under the ‘Delegates’ related list, specifying who can delegate to, and for what duration and scope (assignments, notifications, approvals).
Creating User and Group Accounts via Script
For automation and bulk operations, you can create user and group records programmatically.
- Creating a User:
var userGr = new GlideRecord('sys_user');
userGr.initialize();
userGr.username = 'jdoe';
userGr.firstname = 'John';
userGr.lastName = 'Doe';
userGr.email = 'jdoe@example.com';
userGr.insert();
var newGr = new GlideRecord('sys_user_group');
newGr.initialize();
newGr.name = 'Testing Group';
newGr.manager = 'MANAGER_SYS_ID'; // Replace with a user sys_id
newGr.email = 'testing@example.com';
newGr.description = 'This is a test group.';
newGr.insert();
Adding and Removing Group Members via Script
- Adding a Member:
var grMem = new GlideRecord('sys_user_grmember');
grMem.user = 'USER_SYS_ID'; // Replace with user sys_id
grMem.group = 'GROUP_SYS_ID'; // Replace with group sys_id
grMem.insert();
var grMem = new GlideRecord('sys_user_grmember');
grMem.addQuery('user', 'USER_SYS_ID'); // Replace with user sys_id
grMem.addQuery('group', 'GROUP_SYS_ID'); // Replace with group sys_id
grMem.query();
if (grMem.next()) {
grMem.deleteRecord();
}
Deep Dive: Leveraging the Scratchpad Object
Now, let’s get back to the scratchpad. Its main purpose is to bridge the gap between server-side processing and client-side rendering.
Server-Side: Setting Scratchpad Variables
You can set values on the scratchpad object from server-side scripts, most commonly within Business Rules that run before or after a record is displayed (onDisplay). You can also use it in Script Includes that are called by UI Policies or Client Scripts.
The syntax is straightforward:
// In a server-side script (e.g., an onDisplay Business Rule)
// Set a simple string value
g_scratchpad.myStringValue = 'This is a server-side message!';
// Set a numerical value
g_scratchpad.myNumber = 123;
// Set a boolean value
g_scratchpad.isImportant = true;
// Set a value from a GlideRecord query
var gr = new GlideRecord('sys_user');
gr.get('USER_SYS_ID'); // Replace with an actual sys_id
g_scratchpad.userName = gr.name;
// You can even pass an object, but be mindful of complexity
g_scratchpad.userInfo = {
id: gr.sys_id.toString(),
name: gr.name.toString()
};
Client-Side: Accessing Scratchpad Variables
On the client-side, typically within a UI Policy Script or a Client Script (especially those triggered by onChange or onLoad), you can access these values using the same g_scratchpad object.
// In a client-side script (e.g., a UI Policy script or Client Script)
// Accessing the string value
var serverMessage = g_scratchpad.myStringValue;
alert(serverMessage); // Will show "This is a server-side message!"
// Accessing the number
var number = g_scratchpad.myNumber;
console.log('The number from server is: ' + number);
// Accessing the boolean
if (g_scratchpad.isImportant) {
g_scratchpad.setMandatory('short_description', true); // Example: making a field mandatory
}
// Accessing the user name
var fetchedUserName = g_scratchpad.userName;
g_form.setValue('description', 'This record was processed by: ' + fetchedUserName);
// Accessing the user info object
var userId = g_scratchpad.userInfo.id;
var userNameFromObject = g_scratchpad.userInfo.name;
console.log('User ID from object: ' + userId + ', Name: ' + userNameFromObject);
Practical Use Cases and Examples
1. Dynamic Field Visibility/Mandatory Status
Scenario: You have a ‘Problem’ form. If the ‘Problem Type’ is ‘Hardware Failure’, you want to make the ‘CI’ (Configuration Item) field mandatory. Otherwise, it should be optional.
Solution:
- Business Rule (onDisplay):
// Script in an onDisplay Business Rule on the problem table
(function executeRule(current, previous /*null when async*/) {
if (current.type == 'hardware_failure') { // Assuming 'hardware_failure' is a choice value for 'Type'
g_scratchpad.isCiMandatory = true;
} else {
g_scratchpad.isCiMandatory = false;
}
})(current, previous);
// Script in a UI Policy that runs onLoad for the problem table
function onCondition() {
if (g_scratchpad.isCiMandatory) {
g_form.setMandatory('cmdb_ci', true);
} else {
g_form.setMandatory('cmdb_ci', false);
}
}
onCondition();
Interview Relevance: This demonstrates understanding of server-client interaction, conditional logic, and essential ServiceNow components like Business Rules and UI Policies.
2. Pre-populating Fields with Complex Data
Scenario: When a user creates an ‘Incident’, you want to pre-populate a custom field, say ‘User Department’, with the department of the ‘Caller’ they select. This department might not be directly on the Incident table but can be fetched from the sys_user record of the caller.
Solution:
- Client Script (onChange for Caller field): This script triggers when the Caller field changes.
// onChange Client Script on 'incident' table, field 'caller_id'
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
// Fetch user details on the client, but a more efficient way is server-side
// Let's illustrate with scratchpad for a server-side approach
// The actual efficient way:
// In an onDisplay Business Rule, get the caller's department if it's set.
// Then, pass it to the client via scratchpad.
// For this example, let's assume we directly fetch it on client for simplicity,
// but then refactor to scratchpad.
// Initial client-side fetch (less efficient for complex data)
/*
var userGr = new GlideRecord('sys_user');
userGr.get(newValue);
g_form.setValue('u_user_department', userGr.department.getDisplayValue());
*/
// Using Scratchpad for efficiency:
// Server-side (onDisplay Business Rule):
// var callerSysId = current.caller_id.toString();
// var userGr = new GlideRecord('sys_user');
// if (userGr.get(callerSysId)) {
// g_scratchpad.callerDepartment = userGr.department.getDisplayValue();
// } else {
// g_scratchpad.callerDepartment = ''; // Clear if no caller or department
// }
// Client-side (UI Policy or Client Script onLoad):
if (g_scratchpad.callerDepartment) {
g_form.setValue('u_user_department', g_scratchpad.callerDepartment);
} else {
g_form.setValue('u_user_department', ''); // Clear if scratchpad value is empty
}
}
Interview Tip: When asked about populating fields, always consider performance. Server-side processing via Business Rules and passing data via scratchpad is generally preferred over heavy client-side GlideRecord lookups, especially for large datasets.
3. Conditional Actions in UI Policies
Scenario: You have a ‘Change Request’ form. If the ‘Risk’ is ‘High’ or ‘Critical’, you want to make the ‘Emergency Change’ field checked by default and perhaps make a custom field ‘Justification for High Risk’ mandatory.
Solution:
- Business Rule (onDisplay):
// Script in an onDisplay Business Rule on the change_request table
(function executeRule(current, previous /*null when async*/) {
if (current.risk.toString() === '1' || current.risk.toString() === '2') { // Assuming 1=High, 2=Critical sys_id for risk choice
g_scratchpad.isHighRisk = true;
} else {
g_scratchpad.isHighRisk = false;
}
})(current, previous);
// Script in a UI Policy that runs on form load for change_request
function onCondition() {
if (g_scratchpad.isHighRisk) {
g_form.setValue('emergency_change', true); // Check the emergency change box
g_form.setMandatory('u_justification_high_risk', true); // Make custom field mandatory
} else {
// Optionally, uncheck emergency change and make justification optional if risk changes
g_form.setValue('emergency_change', false);
g_form.setMandatory('u_justification_high_risk', false);
}
}
onCondition();
Troubleshooting Scratchpad Issues
Scratchpad usage is generally robust, but sometimes issues can arise. Here are common pitfalls and how to address them:
Troubleshooting Common Scratchpad Problems
- Scratchpad Variable Not Available on Client:
- Check Business Rule Timing: Ensure your Business Rule is an
onDisplayrule. Scratchpad is primarily populated byonDisplayBusiness Rules or Script Includes called by client scripts. - Check Business Rule Conditions: Verify that the conditions for your Business Rule are met when the form loads. Use logs (
gs.log()) in your Business Rule to confirm execution. - Variable Naming: Double-check for typos in variable names on both server and client sides (
g_scratchpad.myVariable). - Scope: Ensure the Business Rule and the UI Policy/Client Script are in the same scope or properly accessible.
- Check Business Rule Timing: Ensure your Business Rule is an
- Incorrect Data Passed:
- GlideRecord Logic: Review your server-side GlideRecord queries. Are you fetching the correct records and fields? Use
gs.log()to inspect values before assigning them tog_scratchpad. - Reference Fields: When passing sys_ids for reference fields, ensure you’re passing the correct sys_id. For display values, use
.getDisplayValue()on the server.
- GlideRecord Logic: Review your server-side GlideRecord queries. Are you fetching the correct records and fields? Use
- Performance Issues:
- Overuse: Avoid populating the scratchpad with excessive data. Only pass what is absolutely necessary for client-side logic.
- Complex Server-Side Logic: While scratchpad helps, if the server-side script itself is very slow, it will impact form load times. Optimize your server-side queries.
Beyond the Basics: Advanced Considerations
1. Scratchpad with Script Includes
You’re not limited to Business Rules. You can call a Script Include from a Client Script, and within that Script Include, use gs.scratchpad to set values that the calling Client Script can then access.
// Client Script (e.g., onLoad)
function onLoad() {
var result = new MyUtilityScript().getScratchedData();
if (g_scratchpad.specialValue) {
g_form.addInfoMessage("Special instruction: " + g_scratchpad.specialValue);
}
}
// Script Include (MyUtilityScript)
var MyUtilityScript = Class.create();
MyUtilityScript.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getScratchedData: function() {
// This is a simplified example. In a real scenario, you might use AjaxProcessor or GlideAjax.
// However, the concept of setting scratchpad can be demonstrated in a triggered script.
// If this script is called in a context where g_scratchpad is available (e.g., triggered via GlideAjax response handler after a server-side script runs)
// Or more commonly, a server-side script (like BR) sets it, and a client script reads it.
// For direct client-to-server calls fetching data, GlideAjax is preferred.
// The scratchpad's strength is passing data from server-side context (like BR) to client ON FORM LOAD.
// Example of setting scratchpad from a server-side context if called appropriately:
// gs.scratchpad.specialValue = "Important data calculated on server.";
// return true; // Or return a value if needed by GlideAjax
// Let's refine this: scratchpad is typically for form load context.
// If you need dynamic data fetching on client interaction, use GlideAjax.
// For this article's focus on scratchpad, it's best demonstrated via onDisplay BR.
},
type: 'MyUtilityScript'
});
Clarification: While you can technically interact with scratchpad indirectly or in specific contexts within Script Includes, its primary and most powerful use case is for onDisplay Business Rules to pre-populate client-side form elements.
2. Scratchpad vs. GlideAjax
It’s crucial to understand when to use scratchpad and when to use GlideAjax (or its predecessor, GlideRecord.getXMLAnswer()).
- Scratchpad: Ideal for passing initial data from the server to the client when the form loads. It’s a one-time transfer of information.
- GlideAjax: Used for making dynamic server calls from the client, typically in response to user actions (e.g., changing a field value). It allows the client to request specific data from the server at any time and receive a response.
If you need to fetch data based on a user’s interaction after the form has already loaded, GlideAjax is the way to go. If you need to set up the form with data *before* the user even interacts with it, scratchpad is your friend.
Interview Relevance and Best Practices
Understanding the scratchpad is a hallmark of a proficient ServiceNow administrator or developer. It demonstrates an ability to think about server-client architecture and optimize performance.
Key Takeaways for Interviews:
- Define Scratchpad: Explain it as a server-to-client data transfer mechanism for form load.
- Use Cases: Be ready to provide examples like setting field mandatory/read-only status, pre-populating fields, or controlling visibility based on server-side logic.
- Server vs. Client: Clearly articulate where it’s set (server-side, e.g.,
onDisplayBR) and where it’s accessed (client-side, e.g., UI Policy script). - Performance: Emphasize how scratchpad improves performance by reducing client-side processing and multiple server calls.
- vs. GlideAjax: Differentiate its use case from GlideAjax (form load vs. dynamic client-initiated calls).
- Best Practice: Use it judiciously for form load initialization, not for complex real-time interactions.
General Best Practices for Scratchpad Usage:
- Keep it Simple: Pass only the data that is strictly needed on the client.
- Meaningful Names: Use descriptive variable names for scratchpad properties.
- Server-Side Optimization: Ensure the server-side logic populating the scratchpad is efficient.
- Documentation: Comment your scripts, especially Business Rules that populate the scratchpad, explaining what data is being passed and why.
Conclusion
The ServiceNow scratchpad object is a powerful, yet elegantly simple, tool in the arsenal of any ServiceNow professional. By understanding its role in facilitating server-to-client communication during form loads, you can significantly enhance the user experience, streamline workflows, and build more efficient applications.
As you continue your journey through ServiceNow’s ever-evolving landscape, mastering concepts like the scratchpad will not only make your solutions more robust but also position you as a valuable asset in any development or administration team. Happy scripting!