Two questions decide this round more often than any other: what runs when a form loads, and what runs when a form is submitted. Everything else on the client side sits on top of those two sequences. The questions here come from the RizeX Labs ServiceNow question bank and cover client scripts, UI policies, data policies, GlideAjax and UI actions, the block a developer interviewer works through after the ITSM screening. Answers include the order tables, the code you are expected to write on a whiteboard, and the follow-up each question attracts.
Where client code runs, and what it can reach
First, the vocabulary. ServiceNow has four client script types, set by the Type field on the client script record.
| Type | When it runs | Typical use |
|---|---|---|
| onLoad | Once, after the form renders | Defaults, hiding sections, reading g_scratchpad |
| onChange | Every time the named field changes, including once during form load | Reacting to a field the user edited |
| onSubmit | On Submit, Update or any UI action that submits | Validating before data leaves the browser |
| onCellEdit | On inline edit from a list, not from a form | Blocking list edits, where g_form does not exist |
An onChange script is handed five parameters, and candidates lose marks on the fourth one.
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
// isLoading is true while the form is still loading.
// Without this guard the script fires on every form load.
if (isLoading || newValue === '') {
return;
}
if (newValue === '1') {
g_form.setMandatory('business_service', true);
}
}
g_form is the client-side object for the form in front of the user. It reads and writes what is on screen, not what is in the database: getValue(), setValue(), setMandatory(), setVisible(), setReadOnly(), addErrorMessage(), showFieldMsg(), isNewRecord(). Nothing it does is saved until the form is submitted.
g_user holds the logged-in user's details, cached in the session so no server call is needed: g_user.userName, g_user.userID, g_user.firstName, g_user.lastName, plus hasRole(), hasRoleExactly() and hasRoleFromList(). One detail interviewers like: g_user.hasRole('itil') returns true for an admin who does not hold the itil role, because admin counts as every role. Use hasRoleExactly() when the check must be strict.
This is the gap the trainers see most often. Candidates know the syntax but not the execution: when the code runs, where it runs, and what data it can reach. Client-side against server-side is the ServiceNow version of that gap, and the next two questions are where it shows.
Sequence of execution
What is the sequence of execution between ACL, business rules, client script, UI policies while form loads?
The server finishes its work before the browser starts.
| Step | What runs | Where | What it can do |
|---|---|---|---|
| 1 | Display business rules | Server | Run before the form HTML is built. Push values with g_scratchpad so the client needs no GlideAjax call later |
| 2 | Read ACLs | Server | Decide which record and fields the user may read. A field they cannot read never reaches the browser |
| 3 | Form rendered and sent to the browser | Server to client | The HTML carries only the permitted fields |
| 4 | onLoad client scripts | Browser | Defaults, hiding sections, reading g_scratchpad |
| 5 | UI policies | Browser | Mandatory, read-only and visible settings, plus their own scripts |
Two points carry the answer. ACLs are server-side and final: hiding a field with a UI policy is presentation, and a client script cannot protect data the server has already sent. And UI policies run after onLoad client scripts, so where both touch the same field, the UI policy wins.
What is the sequence of execution between client script, business rules, workflow, notifications when form is submitted?
| Step | What runs | Where | Notes |
|---|---|---|---|
| 1 | onSubmit client scripts | Browser | Return false here and nothing below ever happens |
| 2 | Write ACLs | Server | Checked once the data reaches the instance |
| 3 | Before business rules | Server | Run in Order, lowest first. Set current field values here, no current.update() needed |
| 4 | Data policies | Server | Mandatory and read-only rules enforced on the record |
| 5 | Record written to the database | Server | The insert or update happens here |
| 6 | After business rules | Server | Record already saved. Updating current now needs an explicit current.update() |
| 7 | Workflow and Flow Designer | Server | Triggered by the insert or update, so they see the saved record |
| 8 | Async business rules | Server, later | Queued, picked up by the scheduler after commit |
| 9 | Notifications | Server, later | Fired from events, processed by the event queue job after the transaction |
Everything from step 2 is server-side, and everything from step 8 is outside the user's transaction. That is why a user sees "Record updated" before the email has gone out, and why an async business rule cannot stop a save.
Which one will execute first, client script or UI policies?
On form load, the onLoad client script runs first and the UI policy runs after it. If a client script sets a field mandatory and a UI policy sets the same field optional, the field ends up optional, and the developer spends an afternoon looking for the bug in the wrong place.
UI policies also have their own Order field and the Reverse if false checkbox, which undoes the actions when the condition stops matching. A client script has no equivalent, so you write the else branch yourself. ServiceNow's guidance is to use a UI policy wherever one will do the job, and fall back to a client script only for logic a condition builder cannot express.
Client scripts, data policies and cancelling a submit
Can we use current.update() in a client script? Why or why not?
No. current is a server-side GlideRecord object. It exists in business rules, script includes, scheduled jobs and the server half of a UI action. A client script runs in the browser, where that object does not exist, so the call fails.
On the client you work with g_form, which changes what is on the form, and the save happens on submit.
// Wrong. current does not exist in the browser.
current.priority = 1;
current.update();
// Right. Set the value on the form and let the submit save it.
g_form.setValue('priority', 1);
Two follow-ups usually come next. To update a different record from the client, call a script include through GlideAjax and do the update server-side. And in a UI action with the Client checkbox ticked, the client half still cannot use current; it validates with g_form, then calls gsftSubmit(null, g_form.getFormElement(), '<action_name>') to hand control to the server half, which does have current.
How can you cancel a form submission with a client script?
Write an onSubmit client script and return false.
function onSubmit() {
var state = g_form.getValue('state');
var notes = g_form.getValue('close_notes');
if (state === '7' && notes.trim() === '') {
g_form.addErrorMessage('Close notes are required before closing this incident.');
g_form.showFieldMsg('close_notes', 'Enter what was done to resolve this.', 'error');
return false;
}
return true;
}
Returning false stops the submit and leaves the user on the form with their data intact. Say the two conditions out loud: the check must be synchronous, and if the script throws an error before reaching the return, the form submits anyway. Always add the error message, otherwise the user clicks Submit, nothing happens, and they click it again.
What are some drawbacks to using Data Policies instead of UI Policies for required fields?
A data policy runs on the server when the record is written. A UI policy runs in the browser while the user is filling the form. For a required field, the difference is felt by the user.
| UI Policy | Data Policy | |
|---|---|---|
| Where it runs | Browser, as the user types | Server, at insert or update |
| Feedback | Field marked mandatory at once | Error only after Submit |
| Can set visible | Yes | No, mandatory and read-only only |
| Covers imports and web services | No | Yes, when enabled |
So the drawbacks are: the user fills a long form and finds out at the end, the field is never marked mandatory while they work, a data policy cannot show or hide anything, and because it sits at the record level it can also block import sets and integration inserts that legitimately do not carry that field.
They are not competitors. Use a UI policy for the form experience and a data policy when the rule must hold no matter how the record arrives. The Data Policy form has a Use as UI Policy option that generates the matching client behaviour, which is where the interviewer wants you to land.
How to get current date and time in client script?
new Date() gives you the browser clock, in the browser's timezone and format. That is often not what the instance means by now, because the user's timezone and the system date format come from ServiceNow, not from the machine. The dependable pattern is to ask the server.
// Script Include: DateTimeUtils, Client callable checked
var DateTimeUtils = Class.create();
DateTimeUtils.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
getNow: function() {
return new GlideDateTime().getDisplayValue();
},
type: 'DateTimeUtils'
});
// Client script
var ga = new GlideAjax('DateTimeUtils');
ga.addParam('sysparm_name', 'getNow');
ga.getXMLAnswer(function(answer) {
g_form.setValue('u_logged_at', answer);
});
getDisplayValue() returns the time formatted for the logged-in user. If a rough client-side stamp is enough and format does not matter, new Date() is acceptable, provided you can say why you chose it.
Talking about client scripts you have written
Explain any complex client script you have created?
This is where memorised definitions collapse. The interviewer is not testing the API; they are testing whether the work happened. Answer in four parts: situation, approach, reason, result.
A usable shape. Situation: on the Incident form, the business wanted the assignment group and the approving manager filled automatically from category, subcategory and the caller's location. Approach: an onChange script on subcategory calling a client-callable script include through GlideAjax, passing all three values, returning a JSON object with the group sys_id, the group name and the manager. Reason: the mapping table holds around 400 rows, so a client-side lookup was not an option and a UI policy cannot query. Result: the group is right on first save and misrouted tickets in that category dropped.
One detail raises the answer. Set reference fields with both parts, g_form.setValue('assignment_group', sysId, displayName), so the browser does not make an extra server call to resolve the display value.
Did you learn anything interesting while working on client scripts?
Give one specific thing you got wrong and fixed. Some that hold up in an interview:
- onChange scripts fire during form load with
isLoadingtrue. Without the guard, a script meant to react to the user runs for everyone who opens the record. g_form.setValue()on a reference field with only the sys_id triggers a background call to fetch the display value. The third argument removes it.- A client script set to run on the List view can break inline editing, because
g_formis not available there. That work belongs in an onCellEdit script. - Anything you set with
g_formlives only on the form. A record updated by a business rule, an import or an integration never sees it.
UI actions
What is a UI Action?
A UI action is a configurable button, link or context menu item that appears on a form or a list and runs a script when the user selects it. The records live in the sys_ui_action table, each with a table, a condition deciding whether it appears, an Order and a script. The condition field is what makes UI actions worth configuring rather than hard-coding: a Resolve button that shows only when current.state != 6 && gs.hasRole('itil') keeps the form clean for everybody else.
What are the types of UI Actions?
Two things are being asked. First, where the action appears, set by checkboxes on the UI action record:
| Checkbox | Where it appears |
|---|---|
| Form button | Button in the form header |
| Form link | Link under Related Links on the form |
| Form context menu | Right-click menu on the form header |
| List banner button | Button above the list |
| List context menu | Right-click menu on a list row |
| List choice | Action choice list at the bottom of the list, applied to ticked records |
| List link | Link in the list header area |
Second, where the script runs. Leave the Client checkbox clear and the script runs on the server with current, gs and action. Tick it and you supply an Onclick function running in the browser with g_form, usually to confirm or validate, which then calls gsftSubmit() to run the server script on the same record.
Difference between form button and list button?
A form button acts on one record. current is that record, so setting fields and calling current.update() does what you expect.
A list button runs from the list, where there is no single current record. A list banner button runs once for the whole list, so if you need the rows the user ticked, read them on the client with g_list.getChecked(), which returns a comma-separated string of sys_ids, or query the table with GlideRecord on the server and loop. A list choice action is the one aimed at selected records.
The practical consequence: never reuse a form button's script on a list without changing it. One UI action record can have both Form button and List banner button ticked, and the script then has to handle both contexts, which is where the bugs come from.
What the interviewer is actually checking
- Whether you can place a piece of code in the execution order without hesitating. Anyone can define an onLoad script; not everyone can say what ran before it.
- Whether you know the boundary.
currenton the server,g_formin the browser, GlideAjax as the sanctioned bridge. - Whether you reach for the lightest tool. UI policy before client script,
g_scratchpadbefore GlideAjax, before business rule before after business rule. - Whether your project stories survive follow-up questions. Which table, what the requirement was, why this approach and not the other, and what you personally wrote.
- Whether you can debug. "Your onChange script is not firing, what do you check?" tells an interviewer more than any definition, so have the answer ready: the field name, the isLoading guard, the Active and UI Type settings, the view, and the browser console.
Preparing for this round
Work through the two order tables until you can draw them from memory, then write one onSubmit script and one GlideAjax pair by hand with nothing to look at. That is the level this round is set at, and it is reachable in a week of honest practice.
For practice under someone who has sat on the other side of the table, the ServiceNow training programme runs these questions as mock rounds on a live instance, so the project example you give is one you actually built.