Back to list
groeimetai

client-scripts

by groeimetai

🤖 AI-powered ServiceNow development with 400+ MCP tools. Works with Claude, GPT, Gemini, Ollama & 75+ providers. Deploy widgets, manage incidents, automate workflows - all through natural language. Open-source Build Agent alternative.

42🍴 9📅 Jan 23, 2026

SKILL.md


name: client-scripts description: This skill should be used when the user asks to "create client script", "client-side script", "onLoad", "onChange", "onSubmit", "onCellEdit", "g_form", "GlideAjax", "form validation", or any ServiceNow client-side scripting. license: Apache-2.0 compatibility: Designed for Snow-Code and ServiceNow development metadata: author: groeimetai version: "1.0.0" category: servicenow tools:

  • snow_create_client_script
  • snow_find_artifact
  • snow_edit_artifact
  • 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

TypeWhen it RunsUse Case
onLoadForm loadsSet defaults, hide/show fields, initial setup
onChangeField value changesReact to user input, cascading updates
onSubmitForm submittedValidation before save
onCellEditList cell editedValidate inline edits

The g_form API

Getting and Setting Values

// Get field value
var priority = g_form.getValue('priority');
var callerName = g_form.getDisplayValue('caller_id');  // Reference display value

// Set field value
g_form.setValue('priority', '1');
g_form.setValue('assigned_to', userSysId, 'John Smith');  // Reference with display

// Clear a field
g_form.clearValue('assignment_group');

Field Visibility and State

// Show/Hide fields
g_form.setVisible('u_internal_notes', false);
g_form.setDisplay('u_internal_notes', false);  // Removes from DOM

// Make field mandatory
g_form.setMandatory('short_description', true);

// Make field read-only
g_form.setReadOnly('caller_id', true);

// Disable field (grayed out but visible)
g_form.setDisabled('state', true);

Messages and Validation

// Field-level messages
g_form.showFieldMsg('email', 'Invalid email format', 'error');
g_form.hideFieldMsg('email');

// Form-level messages
g_form.addInfoMessage('Record saved successfully');
g_form.addErrorMessage('Please fix the errors below');
g_form.clearMessages();

// Flash a field to draw attention
g_form.flash('priority', '#ff0000', 0);  // Red flash

Sections and Labels

// Collapse/Expand sections
g_form.setSectionDisplay('notes', false);  // Collapse
g_form.setSectionDisplay('notes', true);   // Expand

// Change field label
g_form.setLabelOf('short_description', 'Issue Summary');

Common Patterns

Pattern 1: onLoad - Set Defaults

function onLoad() {
  // Only on new records
  if (g_form.isNewRecord()) {
    // Set default priority
    g_form.setValue('priority', '3');

    // Set caller to current user
    g_form.setValue('caller_id', g_user.userID);

    // Hide internal fields from end users
    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) {
  // Don't run during form load
  if (isLoading) return;

  // When category changes, clear subcategory
  if (newValue != oldValue) {
    g_form.setValue('subcategory', '');
    g_form.clearValue('u_item');
  }

  // Auto-set priority based on category
  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;

  // Get data from server
  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);

    // Update form with server data
    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() {
  // Validate email format
  var email = g_form.getValue('u_email');
  if (email && !isValidEmail(email)) {
    g_form.showFieldMsg('u_email', 'Please enter a valid email', 'error');
    return false;  // Prevent submit
  }

  // Require close notes when resolving
  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;
  }

  // Confirm before high-priority submission
  var priority = g_form.getValue('priority');
  if (priority == '1') {
    return confirm('This will create a Priority 1 incident. Continue?');
  }

  return true;  // Allow submit
}

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;

  // Category "Hardware" requires asset tag
  var isHardware = newValue == 'hardware';
  g_form.setMandatory('u_asset_tag', isHardware);
  g_form.setDisplay('u_asset_tag', isHardware);

  // Category "Software" requires application name
  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

// Current user information
var userName = g_user.userName;       // User name
var userID = g_user.userID;           // sys_id
var firstName = g_user.firstName;      // First name
var lastName = g_user.lastName;        // Last name
var fullName = g_user.getFullName();   // Full name

// Role checks
if (g_user.hasRole('admin')) { }
if (g_user.hasRole('itil')) { }
if (g_user.hasRoleExactly('incident_manager')) { }  // Exact match, no admin override

// Multiple roles
if (g_user.hasRoleFromList('itil,incident_manager')) { }

Performance Best Practices

1. Minimize Server Calls

// ❌ BAD - Multiple GlideAjax calls
onChange: getUserLocation();
onChange: getUserDepartment();
onChange: getUserManager();

// ✅ GOOD - Single call returning all data
onChange: getUserDetails();  // Returns location, department, manager

2. Use isLoading Parameter

function onChange(control, oldValue, newValue, isLoading) {
  // ❌ BAD - Runs during form load
  callServer(newValue);

  // ✅ GOOD - Skip during load
  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);  // Wait 300ms for typing to stop
}

Common Mistakes

MistakeProblemSolution
Forgetting isLoading checkScript runs unnecessarily on loadAlways check if (isLoading) return;
Blocking onSubmitUI freezes on slow validationUse async validation with callback
No error handling in GlideAjaxSilent failuresAdd error callbacks
Testing only in one browserCross-browser issuesTest Chrome, Firefox, Edge
Direct DOM manipulationBreaks with UI updatesUse g_form API

Score

Total Score

75/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon