| name | client-scripts |
| description | Write ServiceNow client scripts (onLoad/onChange/onSubmit/onCellEdit) using g_form, g_user, GlideAjax, field visibility/mandatory toggles, and validation with debounced server calls. |
| license | Apache-2.0 |
| compatibility | Designed for Snow-Code and ServiceNow development |
| metadata | {"author":"serac","version":"1.0.0","category":"servicenow"} |
| tools | ["snow_create_client_script","snow_artifact_manage","snow_create_script_include"] |
Client Script Patterns for ServiceNow
Client Scripts run in the user's browser and control form behavior. Unlike server-side scripts, client scripts can use modern JavaScript (ES6+) in modern browsers.
Client Script Types
| Type | When it Runs | Use Case |
|---|
| onLoad | Form loads | Set defaults, hide/show fields, initial setup |
| onChange | Field value changes | React to user input, cascading updates |
| onSubmit | Form submitted | Validation before save |
| onCellEdit | List cell edited | Validate inline edits |
The g_form API
Getting and Setting Values
var priority = g_form.getValue("priority")
var callerName = g_form.getDisplayValue("caller_id")
g_form.setValue("priority", "1")
g_form.setValue("assigned_to", userSysId, "John Smith")
g_form.clearValue("assignment_group")
Field Visibility and State
g_form.setVisible("u_internal_notes", false)
g_form.setDisplay("u_internal_notes", false)
g_form.setMandatory("short_description", true)
g_form.setReadOnly("caller_id", true)
g_form.setDisabled("state", true)
Messages and Validation
g_form.showFieldMsg("email", "Invalid email format", "error")
g_form.hideFieldMsg("email")
g_form.addInfoMessage("Record saved successfully")
g_form.addErrorMessage("Please fix the errors below")
g_form.clearMessages()
g_form.flash("priority", "#ff0000", 0)
Sections and Labels
g_form.setSectionDisplay("notes", false)
g_form.setSectionDisplay("notes", true)
g_form.setLabelOf("short_description", "Issue Summary")
Common Patterns
Pattern 1: onLoad - Set Defaults
function onLoad() {
if (g_form.isNewRecord()) {
g_form.setValue("priority", "3")
g_form.setValue("caller_id", g_user.userID)
if (!g_user.hasRole("itil")) {
g_form.setVisible("assignment_group", false)
g_form.setVisible("assigned_to", false)
}
}
}
Pattern 2: onChange - Cascading Updates
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return
if (newValue != oldValue) {
g_form.setValue("subcategory", "")
g_form.clearValue("u_item")
}
if (newValue == "security") {
g_form.setValue("priority", "1")
g_form.setReadOnly("priority", true)
} else {
g_form.setReadOnly("priority", false)
}
}
Pattern 3: onChange with GlideAjax
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || newValue == "") return
var ga = new GlideAjax("MyScriptInclude")
ga.addParam("sysparm_name", "getUserDetails")
ga.addParam("sysparm_user_id", newValue)
ga.getXMLAnswer(function (response) {
var data = JSON.parse(response)
g_form.setValue("location", data.location)
g_form.setValue("department", data.department)
g_form.setValue("u_vip", data.vip)
if (data.vip == "true") {
g_form.setValue("priority", "1")
g_form.flash("priority", "#ffff00", 2)
}
})
}
Pattern 4: onSubmit - Validation
function onSubmit() {
var email = g_form.getValue("u_email")
if (email && !isValidEmail(email)) {
g_form.showFieldMsg("u_email", "Please enter a valid email", "error")
return false
}
var state = g_form.getValue("state")
var closeNotes = g_form.getValue("close_notes")
if (state == "6" && !closeNotes) {
g_form.showFieldMsg("close_notes", "Close notes required", "error")
g_form.setMandatory("close_notes", true)
return false
}
var priority = g_form.getValue("priority")
if (priority == "1") {
return confirm("This will create a Priority 1 incident. Continue?")
}
return true
}
function isValidEmail(email) {
var regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return regex.test(email)
}
Pattern 5: Conditional Mandatory Fields
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return
var isHardware = newValue == "hardware"
g_form.setMandatory("u_asset_tag", isHardware)
g_form.setDisplay("u_asset_tag", isHardware)
var isSoftware = newValue == "software"
g_form.setMandatory("u_application", isSoftware)
g_form.setDisplay("u_application", isSoftware)
}
GlideAjax Pattern (Server Communication)
Client Script
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading || !newValue) return
var ga = new GlideAjax("IncidentUtils")
ga.addParam("sysparm_name", "getRelatedIncidents")
ga.addParam("sysparm_ci", newValue)
ga.getXMLAnswer(handleResponse)
}
function handleResponse(response) {
var result = JSON.parse(response)
if (result.count > 0) {
g_form.addWarningMessage("There are " + result.count + " related open incidents for this CI")
}
}
Server Script Include
var IncidentUtils = Class.create()
IncidentUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getRelatedIncidents: function () {
var ci = this.getParameter("sysparm_ci")
var result = { count: 0, incidents: [] }
var gr = new GlideRecord("incident")
gr.addQuery("cmdb_ci", ci)
gr.addQuery("active", true)
gr.query()
result.count = gr.getRowCount()
while (gr.next()) {
result.incidents.push({
number: gr.getValue("number"),
short_description: gr.getValue("short_description"),
})
}
return JSON.stringify(result)
},
type: "IncidentUtils",
})
g_user Object
var userName = g_user.userName
var userID = g_user.userID
var firstName = g_user.firstName
var lastName = g_user.lastName
var fullName = g_user.getFullName()
if (g_user.hasRole("admin")) {
}
if (g_user.hasRole("itil")) {
}
if (g_user.hasRoleExactly("incident_manager")) {
}
if (g_user.hasRoleFromList("itil,incident_manager")) {
}
Performance Best Practices
1. Minimize Server Calls
onChange: getUserLocation()
onChange: getUserDepartment()
onChange: getUserManager()
onChange: getUserDetails()
2. Use isLoading Parameter
function onChange(control, oldValue, newValue, isLoading) {
callServer(newValue)
if (isLoading) return
callServer(newValue)
}
3. Debounce Rapid Changes
var timeout
function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return
clearTimeout(timeout)
timeout = setTimeout(function () {
performExpensiveOperation(newValue)
}, 300)
}
Common Mistakes
| Mistake | Problem | Solution |
|---|
Forgetting isLoading check | Script runs unnecessarily on load | Always check if (isLoading) return; |
| Blocking onSubmit | UI freezes on slow validation | Use async validation with callback |
| No error handling in GlideAjax | Silent failures | Add error callbacks |
| Testing only in one browser | Cross-browser issues | Test Chrome, Firefox, Edge |
| Direct DOM manipulation | Breaks with UI updates | Use g_form API |