Mastering Client Scripting: Top Interview Questions You Need to Ace
In the dynamic world of ServiceNow development, client-side scripting is often where the magic happens. It’s how we make forms interactive, provide instant feedback, and create seamless user experiences. If you’re eyeing a role as a ServiceNow developer, mastering client-side concepts isn’t just an advantage—it’s a necessity. Interviewers love to probe your understanding here, separating the true practitioners from those who merely skim the surface.
This article is your comprehensive guide to the most common and critical client script interview questions. We’ll break down complex topics into digestible explanations, provide practical scenarios, and arm you with the knowledge to confidently tackle any client-side challenge thrown your way.
Client Scripts: The Foundation of Form Interactivity
Let’s start with the basics. Client scripts are pieces of JavaScript code that execute directly in the user’s browser, allowing for real-time interaction with forms and lists.
What are Client Scripts and Their Types?
A fundamental question! Client scripts are JavaScript snippets that run on the client side (in the browser). They are crucial for enhancing user experience by making forms dynamic and responsive without needing a round trip to the server for every interaction. There are four primary types:
- OnLoad: These scripts execute every time a form loads. Think of them as the initial setup crew for your form.
- OnChange: Triggered when a value in a specific field changes. They also run when the form initially loads, which is a common point of confusion and an important interview nuance.
- OnSubmit: These scripts run just before a form is submitted to the server. They are perfect for last-minute validations.
- OnCellEdit: Unlike the others, these don’t work on forms. Instead, they run when you change a value in a cell within a list view.
Understanding `OnChange` Client Script Parameters
When you’re writing an OnChange script, ServiceNow provides you with several powerful parameters automatically. Knowing these can help you write efficient and robust scripts:
control: This gives you access to the HTML DOM element of the field the script is applied to. While tempting, a best practice is to avoid direct DOM manipulation; useg_forminstead.oldValue: The value of the field *before* the current change.newValue: The *new* value of the field after the change. This is incredibly useful for comparing states.isLoading: A Boolean (true/false) indicating if the form is currently loading. This is vital becauseOnChangescripts also run on form load.isTemplate: Used when you’re populating a form using a template rather than manual input.
Interview Tip: A common trick question is, “Can we make an OnChange client script work on form load only for initial setup but not on subsequent changes?” The answer involves leveraging the isLoading parameter. You’d typically wrap your core logic like this:
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return; // Don't run the script logic if the form is loading or the field is empty
}
// Your actual onChange logic here
}Conversely, if you want an OnChange script to run on load *and* on every change, you simply remove the if (isLoading || newValue === '') { return; } condition.
Stopping Form Submission
This is a critical function for validation. How do you stop form submission using an OnSubmit client script? Simple: return false;. For server-side validation, you’d use current.setAbortAction(true); in a before Business Rule.
`OnCellEdit` Script Parameters
Less common, but still important for list editing, are the OnCellEdit parameters:
sysIDs: An array of the sys_ids of the records whose cells were edited.table: The name of the table being edited.oldValues: An array of the original values in the edited cells.newValues: An array of the new values in the edited cells.callback: A function to call after your script finishes, typically used to confirm or deny the edit.
Best Practices for Client-Side Scripting
Interviewers love to hear about best practices. It shows you’re thinking beyond just making things work:
- Enclose Code in Functions: Always, always, always. Avoid global variables and functions if possible.
- Avoid DOM Manipulation: Resist the urge to use
document.getElementById(). Stick tog_form. Why? Because ServiceNow’s DOM structure can change with upgrades, breaking your scripts.g_formprovides an abstraction layer that’s more stable. - Avoid Global Client Scripting: Keep your scripts scoped to the forms they affect.
- Minimize `g_form.getReference()`: This is a synchronous call (by default) and can lock up the browser, leading to a poor user experience. Use GlideAjax for asynchronous data retrieval instead.
- Limit Alerts: While useful for debugging, too many
alert()calls can annoy users and disrupt workflow. Useg_form.addInfoMessage()orconsole.log()for debugging. - Prefer Asynchronous Calls: Whenever possible, use asynchronous GlideAjax calls to keep the UI responsive.
- Leverage UI Policies/Data Policies first: If you can achieve the desired functionality without scripting, do it!
The “Isolate Script” Checkbox
A crucial setting in client scripts. When checked, the script runs in its own isolated environment. This prevents it from being affected by, or affecting, other scripts on the form. It ensures predictability and reliability, reducing potential conflicts. Generally, it’s good practice to keep it checked unless you specifically need scripts to interact with global variables or functions defined elsewhere, but then you’d be violating a best practice.
UI Policies: The No-Code/Low-Code Powerhouse
UI Policies are a fantastic way to control form behavior without writing a single line of code, making them a preferred choice for simple field manipulations.
What are UI Policies?
UI Policies are rules that allow you to dynamically change form information. They can make fields mandatory, read-only, visible/hidden, and even show/hide related lists based on certain conditions—all on the client side.
Key UI Policy Checkboxes
These checkboxes dictate how and when your UI Policy fires, and they are common interview points:
- Global: When checked, the UI Policy applies to all views of the form. Uncheck it, and you’ll specify a particular view for the policy to apply to.
- Reverse if false: This is a powerful checkbox! If selected, when the UI Policy’s condition evaluates to false, the actions (e.g., making a field mandatory) are automatically reversed. If unchecked, you need to define two UI Policies (one for true, one for false) or use script.
- On Load: If checked, the UI Policy’s conditions and actions are evaluated immediately when the form loads. If unchecked, the policy only triggers when a field changes, meeting its conditions. This means if you want initial form states to be governed by the UI Policy, `On Load` must be checked.
- Inherit: When checked, this UI Policy will also apply to any child tables that extend the table on which the policy is defined.
Can You Write a Script in a UI Policy?
Absolutely! While UI Policies are primarily no-code, they offer the flexibility to run scripts. To do this, you need to enable the “Run scripts” checkbox. This will expose a script field where you can write client-side JavaScript that executes when the UI Policy’s conditions are met (or not met, if “Reverse if false” is active).
Client vs. Server: UI Policies, Data Policies, and APIs
Understanding where functionality executes (client vs. server) and how they communicate is crucial for building robust applications.
UI Policies vs. Data Policies: The Core Differences
This is a frequently asked question, and for good reason! It tests your understanding of execution context and data integrity:
- Execution Side: UI Policies work exclusively on the client side (browser). Data Policies work on both the client side (form) and the server side (any data source, including imports, web services, etc.).
- Field Hiding: You can hide a field on a form using a UI Policy. You cannot hide a field using a Data Policy; you can only make it mandatory or read-only.
- Scripting: You can write scripts in UI Policies (by checking “Run scripts”). You cannot write scripts directly in Data Policies.
- Views: UI Policies can be applied to specific views. Data Policies apply across all views, and all data sources.
- Scope: UI Policies affect only forms. Data Policies affect all sources of data input (forms, list edits, imports, web services, etc.), enforcing data integrity universally.
Interview Relevance: When deciding between a UI Policy and a Data Policy, ask yourself: “Is this rule about user experience on the form, or is it about enforcing data consistency regardless of how the data gets in?” If it’s the latter, a Data Policy (or a Business Rule) is likely the better choice.
When to convert UI Policy to Data Policy (and vice versa): Convert a UI Policy to a Data Policy when you need universal data enforcement (mandatory/read-only) and are not concerned with view-specific behavior, hiding fields, or complex scripting.
Client-Side APIs: Your Toolkit
ServiceNow provides several JavaScript APIs to interact with the platform from the client side. Knowing these is non-negotiable.
Here are some key client-side APIs:
GlideUser(`g_user`)GlideForm(`g_form`)GlideAjaxGlideModalGlideModalFormGlideDialogWindow(older, but still functional)GlideListGlideNavigation(`g_navigation`)GlideScratchpad(`g_scratchpad`)
GlideUser API (`g_user`)
This API is your gateway to information about the currently logged-in user and their roles. It’s accessible via g_user. For example, to get the current user’s system ID, you’d use g_user.userID;.
Other useful methods include:
g_user.firstName;g_user.lastName;g_user.getFullName();g_user.userName;g_user.hasRole('itil');(returns true/false)
GlideForm API (`g_form`)
The g_form API is arguably the most frequently used client-side API. It allows you to inspect and modify forms dynamically.
Common `g_form` methods:
g_form.addInfoMessage("Your message here");: Displays a blue information message.g_form.addErrorMessage("An error occurred!");: Displays a red error message.g_form.setMandatory("field_name", true/false);: Makes a field mandatory or optional.g_form.setReadOnly("field_name", true/false);: Makes a field read-only or editable.g_form.setVisible("field_name", true/false);: Hides or shows a field.g_form.setDisplay("field_name", true/false);: Hides or shows a field and its associated space.g_form.getValue("field_name");: Gets the backend value (sys_id for reference, choice value for choice lists).g_form.getDisplayValue("field_name");: Gets the user-friendly display value of a field.g_form.setValue("field_name", "new_value");: Sets the value of a field.g_form.enableAttachments();: Enables attachments on the form.g_form.addOption("field_name", "value", "label");: Adds a new option to a choice or select box field.g_form.getSectionNames();: Returns an array of all section names on the form.g_form.setSectionDisplay("section_name", true/false);: Hides or shows an entire form section.g_form.hideRelatedLists();: Hides all related lists on the form.g_form.getEditableFields();: Returns an array of all currently editable field names.
`setVisible` vs. `setDisplay`: A classic interview question! While both hide a field, setVisible('u_field', false); hides the field but retains its allocated space on the form, leaving a gap. setDisplay('u_field', false); hides the field *and* removes its space, collapsing the layout for a cleaner look. Generally, setDisplay is preferred for permanent hiding.
Practical Example: How to make all editable fields read-only:
var fields = g_form.getEditableFields();
for (var i = 0; i < fields.length; i++) {
g_form.setReadOnly(fields[i], true);
}GlideAjax: Bridging Client and Server
GlideAjax is a client-side API used to make asynchronous (or synchronous) calls to the server to retrieve data or execute server-side logic from a client script. It’s the recommended way to fetch data from the server database to the client side without using `g_form.getReference()`.
`sysparm_name` in GlideAjax: This parameter is crucial because it tells the Script Include which function to execute. Think of it as the entry point to your server-side logic.
Types of GlideAjax: Synchronous vs. Asynchronous
This is a critical distinction that impacts user experience:
- Asynchronous GlideAjax (`getXMLAnswer`): This sends a request to the server and continues executing the rest of the client script without waiting for a response. When the server responds, a specified callback function handles the data. This is generally preferred because it keeps the UI responsive, allowing users to continue interacting with the form.
- Synchronous GlideAjax (`getXMLWait`): This sends a request to the server and pauses the execution of the client script until a response is received. This blocks the user interface and can lead to a poor user experience if the server takes time to respond. It should be used very sparingly, typically only when the subsequent client-side logic absolutely depends on the server response *before* anything else can happen (e.g., blocking form submission based on server validation).
Example of Asynchronous GlideAjax:
var ga = new GlideAjax('YourScriptInclude'); // Name of your Script Include
ga.addParam('sysparm_name', 'yourFunction'); // Function in your Script Include
ga.addParam('sysparm_caller_id', g_form.getValue('caller_id')); // Pass client data
ga.getXMLAnswer(callbackFunction); // Specify callback for response
function callbackFunction(response) {
var answer = response; // The data returned from the server
// Process the response here, e.g., g_form.setValue()
}Example of Synchronous GlideAjax:
var ga = new GlideAjax('HelloWorld');
ga.addParam('sysparm_name', 'helloWorld');
ga.addParam('sysparm_user_name', g_user.userName);
ga.getXMLWait(); // Waits for response
var answer = ga.getAnswer(); // Get response directly
alert(answer); // Use the answer immediately`gsftSubmit()` in UI Actions
UI Actions, by default, are server-side but can be client-side by checking the “Client” checkbox. Sometimes, you need a UI Action to perform client-side validation *before* executing server-side logic. This is where `gsftSubmit()` comes in:
function clientSideValidation() {
// Perform client-side validation here
if (g_form.getValue('short_description') == '') {
g_form.addErrorMessage('Short description is mandatory!');
return false; // Stop execution
}
// If validation passes, call gsftSubmit to trigger the server-side part of the UI Action
gsftSubmit(null, g_form.getFormElement(), 'ui_action_id_or_name');
}gsftSubmit(null, g_form.getFormElement(), 'action_name'); triggers the UI Action again, but this time only its server-side script executes. The `action_name` is typically found in the UI Action’s properties.
Getting Values from Server to Client (and vice-versa)
Besides GlideAjax, how else can you get server data to the client?
Four common ways:
- GlideAjax: (As discussed) The most flexible and recommended method for on-demand server data retrieval.
- `g_form.getReference()`: Fetches a GlideRecord object for a reference field. It’s often synchronous (unless configured otherwise), leading to potential UI blocking. Use GlideAjax for better performance.
- `g_scratchpad`: (Discussed below) A powerful object passed from Display Business Rules.
- Display Business Rules: The mechanism through which `g_scratchpad` works.
Display Business Rules and `g_scratchpad`
This is a fundamental concept for performance optimization! A Display Business Rule runs on the server *before* a form loads, but *after* Query Business Rules and *before* `OnLoad` client scripts and `OnLoad` UI Policies. Its primary objective is to populate the `g_scratchpad` object with server-side data that client scripts might need.
The `g_scratchpad` is a shared object that is automatically sent to the client as part of the form. This avoids multiple GlideAjax calls for data that is needed immediately upon form load, significantly improving performance.
Example: In a Display BR:
g_scratchpad.callerVIP = current.caller_id.vip; // Pass VIP status of caller
g_scratchpad.priorityLevel = current.priority; // Pass priorityThen, in an OnLoad client script:
if (g_scratchpad.callerVIP == 'true') {
g_form.addInfoMessage('Caller is a VIP!');
}
if (g_scratchpad.priorityLevel == 1) {
g_form.addErrorMessage('High priority ticket!');
}When do Display BRs run? Before `OnLoad` client scripts and `OnLoad` UI Policies.
Advanced Client-Side UI Elements: Pages, Macros, and Modals
Beyond standard forms, ServiceNow allows for extensive UI customization using UI Pages, UI Macros, and various modal dialogs.
UI Pages
UI Pages are essentially custom web pages built within ServiceNow using HTML, CSS, and Jelly scripting. They are incredibly versatile for creating custom interfaces, dashboards, or interactive forms that don’t fit the standard form layout.
- Calling UI Pages from Client Script: You can open UI Pages in modal dialogs using
GlideDialogWindow(older, but functional) or the more modernGlideModalAPI. - Syntax Example:
var gdw = new GlideModal('ui_page_name'); // Or new GlideDialogWindow gdw.setTitle('My Custom Dialog'); gdw.setSize(600, 400); // Width, Height gdw.setPreference('keyid', g_form.getUniqueValue()); // Pass data to UI Page gdw.render(); - Passing Values to UI Pages: Use the
setPreference()method ofGlideModalorGlideDialogWindow. - Accessing Values in UI Pages: Inside the UI Page’s Jelly script, you can retrieve passed values using
<g:evaluate var="jvar_my_var" expression="RP.getWindowProperties().get('keyid')"/>. - Processing Script: UI Pages can have a “Processing Script,” which is server-side JavaScript. This script executes when a form within the UI Page (enclosed in
<g:ui_form></g:ui_form>tags) is submitted. It’s similar to a UI Action’s server-side script. - Closing a UI Page: Use
GlideDialogWindow.get().destroy();within a client script on the UI Page. - Accessing HTML values in UI Pages (Client-side): Use
gel(), which is an alias fordocument.getElementById(), e.g.,var myValue = gel('my_element_id').value;.
UI Macros
UI Macros are reusable components, often written with Jelly scripting, used to create custom controls or interfaces on forms. Think of them as custom HTML snippets that can enhance your form’s functionality (e.g., custom buttons, radio button groups, or complex input fields).
- Calling UI Macros on the Form:
- Create a UI Formatter.
- In the formatter, reference your UI Macro using
macro_name.xml. - Add the formatter to the form via Form Layout.
- Calling UI Macros on the Dictionary: In the dictionary attributes for a field, add
ref_contributions=macro_name. This places the macro next to the field. - Hiding UI Macros using Script: You need to use JavaScript DOM manipulation for this. It’s an exception to the “avoid DOM manipulation” rule because UI Macros don’t have direct
g_formcontrol.document.getElementById('uimacro_id').style.display = 'none'; // Hide document.getElementById('uimacro_id').style.display = 'block'; // ShowCrucially, for this to work, the “Isolate script” checkbox on your client script (or UI Policy script) must be unchecked.
- Calling UI Pages from UI Macros: Embed a client script within the UI Macro using `<script>` tags. Create a button and use its `onclick` attribute to call a function defined in that script, which then uses
GlideModalto open the UI Page.
`GlideModal` vs. `GlideModalForm`
- `GlideModal` (or `GlideDialogWindow`): Used to call UI Pages from client-side scripts. It allows you to present a custom HTML page in a modal dialog.
- `GlideModalForm`: Used to directly open a form for a *table record* (e.g., a new Incident form, or an existing User record form) in a modal dialog from client-side scripts (Client UI Actions, Client Scripts, UI Policies, UI Macros). It’s incredibly useful for quickly capturing or displaying record-specific information without leaving the current form.
Catalog-Specific Client Scripting: Variables and Variable Sets
Service Catalog items have their own unique scripting considerations.
Variables and Variable Sets
- Variables: These are the “fields” within a catalog item, such as Single Line Text, Multi Line Text, Reference, Select Box, Multiple Choice, etc.
- Variable Sets: A collection of variables that can be reused across multiple catalog items. This saves time and ensures consistency. If you need to modify a variable in a set, the changes propagate to all associated catalog items.
- Single Row vs. Multi-Row Variable Set (MRVS):
- Single Row: Captures data for a single entity (e.g., one user’s details).
- Multi-Row: Presents variables in a grid layout, allowing users to add multiple “rows” of data (e.g., adding multiple software licenses, each with its own cost and quantity).
- Reading Values from an MRVS:
var mrvsRows = g_form.getValue("mrvs_variable_set_name"); // The above returns a JSON string, which needs to be parsed var oMrvs = JSON.parse(mrvsRows); var totalCost = 0; for (var i in oMrvs) { totalCost += parseFloat(oMrvs[i]["cost_field_name"]); // Ensure numerical conversion } g_form.setValue("total_cost_summary_field", totalCost);
Catalog UI Policies vs. Normal UI Policies
Catalog UI Policies are specifically designed for Service Catalog items. The key differences:
- Catalog UI Policies do not control views.
- They work across the catalog item form, the requested item form, and catalog task forms.
- They have specific checkboxes like “Applies on Catalog Item,” “Applies on Requested Item,” “Applies on Catalog Task,” and “Applies on Target Record.”
Applying UI Policy to a Record Producer: To make a Catalog UI Policy work only on a Record Producer’s target record form (e.g., controlling fields on an Incident created by a Record Producer), ensure “Applies on Target Record” is checked.
Applying UI Policy to Catalog Tasks: To make a Catalog UI Policy work only on Catalog Tasks, check “Applies on Catalog Task.”
Catalog Client Scripts vs. Normal Client Scripts
Similarly, Catalog Client Scripts are tailored for the Service Catalog:
- Normal client scripts have 4 types; Catalog Client Scripts typically have 3 (
OnLoad,OnChange,OnSubmit).OnCellEditis not applicable for catalog item forms. - Catalog Client Scripts work on Catalog Items, Requested Items, and Catalog Tasks.
- Normal Client Scripts work on views; Catalog Client Scripts do not work on views.
Cascade Variable
The “Cascade variable” checkbox in an Order Guide allows variables with matching names to automatically update their values across all associated catalog items within that Order Guide. This streamlines the ordering process by propagating common input values.
Rule: The variable name in the Order Guide must exactly match the variable name in the catalog item for cascading to work.
Client APIs in Record Producer Script?
No! A Record Producer’s script runs on the server side (after submission). Client-side APIs like `g_form` or `g_user` are not available in this context. You’d use server-side APIs like `current` or `gs`.
Execution Order and Troubleshooting
Understanding the order of execution is paramount for debugging and predicting behavior.
Which Runs First: Client Script or UI Policy?
A classic! Client scripts run first, and UI Policies run last (for the same type, e.g., OnLoad Client Script then OnLoad UI Policy). This means a UI Policy can override actions taken by a client script if they modify the same field. Keep this in mind during troubleshooting!
The Full Script Execution Order
This is an exhaustive list and a great way to impress in an interview:
- Query Business Rules
- Display Business Rules (populating `g_scratchpad`)
- `OnLoad` Client Scripts
- `OnLoad` UI Policies
- `OnChange` Client Scripts
- `OnChange` UI Policies
- `OnSubmit` Client Scripts
- Client-side UI Actions
- Server-side UI Actions (triggered by `gsftSubmit` or direct click)
- Before Business Rules (order < 1000)
- Before Engines (Approval, Assignment, Data Policy, Escalation, etc.)
- Before Business Rules (order >= 1000)
- Database Operation (Insert, Update, Delete)
- After Business Rules (order < 1000)
- After Engines (Label, Listener, Table Notifications, Email, etc.)
- Email Notifications (based on weight)
- After Business Rules (order >= 1000)
Troubleshooting Dynamic Filters
Making a filter dynamic on a list collector variable based on another field (e.g., a reference field) is a common requirement. You’ll typically use an OnChange client script and modify the query of the list collector.
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
var collectorName = 'list_collector_variable_name'; // Internal name of your List Collector
var dynamicQuery = "u_order=" + newValue; // Assuming 'newValue' is the order sys_id
try {
var myListCollector = g_list.get(collectorName); // For newer versions/frameworks
myListCollector.reset();
myListCollector.setQuery(dynamicQuery);
} catch (e) {
// Fallback for older versions or different frameworks
window[collectorName + 'g_filter'].reset();
window[collectorName + 'g_filter'].setQuery(dynamicQuery);
window[collectorName + 'acRequest'](null);
}
}This script dynamically adjusts the query for the list collector based on the value of another field, offering a real-time filtering experience.