ServiceNow Workflow Automation: Streamline Operations & Boost Efficiency






Mastering Workflow Automation in ServiceNow: A Deep Dive into GlideRecord and GlideForm


Mastering Workflow Automation in ServiceNow: A Deep Dive into GlideRecord and GlideForm

In today’s fast-paced digital landscape, businesses are constantly seeking ways to streamline operations, reduce manual effort, and boost efficiency. Enter ServiceNow – a powerful platform renowned for its ability to transform enterprise workflows. While ServiceNow offers robust out-of-the-box capabilities, true automation often requires a touch of customization. This is where scripting, specifically leveraging the Glide API, becomes indispensable.

This article will take you on a journey through the heart of ServiceNow scripting: the Glide API. We’ll demystify two of its most crucial components, GlideRecord for server-side operations and GlideForm for client-side interactions, providing practical insights and real-world examples to empower your workflow automation efforts. Whether you’re a seasoned developer or just starting, understanding these APIs is key to unlocking ServiceNow’s full potential.

The Power of Workflow Automation in ServiceNow

Workflow automation in ServiceNow isn’t just about making things faster; it’s about making them smarter. It involves automating repetitive tasks, orchestrating complex processes, and ensuring data consistency across the platform. From incident management to HR onboarding, automated workflows can drastically cut down processing times, minimize human error, and free up your teams to focus on strategic initiatives. While many automations can be built using Flow Designer or Orchestration, complex scenarios or very specific UI/data manipulations often necessitate scripting. And when it comes to scripting, the Glide API is your best friend.

Understanding the Glide API: Your Gateway to Customization

Think of the Glide API as a comprehensive toolkit for ServiceNow developers. It’s a collection of JavaScript classes and methods that allow you to interact with the platform’s core functionalities – from database operations to UI manipulation – in a structured and efficient way. Instead of writing raw SQL queries or complex browser-side DOM manipulations, Glide API provides a higher-level, ServiceNow-specific abstraction that simplifies development and ensures platform compatibility.

Why is it so important? Because it allows you to:

  • Customize Default Behavior: Tailor applications to meet unique business requirements.
  • Interact with the Database: Perform CRUD (Create, Read, Update, Delete) operations on records without SQL.
  • Enhance User Experience: Dynamically control form elements, messages, and visibility.
  • Integrate with Other Systems: Facilitate data exchange and process orchestration.

Client-Side vs. Server-Side Glide APIs

The Glide API isn’t a monolith; it’s intelligently divided into client-side and server-side components. This distinction is fundamental to understanding where and how to apply your scripts:


Client-Side (Runs in Browser)Server-Side (Runs on ServiceNow Server)
GlideForm (g_form)GlideRecord
GlideUser (g_user)GlideSystem (gs)
GlideAjaxGlideDate
GlideDialogWindowGlideDateTime
GlideListGlideAggregation
GlideMenuGlideElement

This article will focus on GlideRecord for server-side logic and GlideForm for client-side UI interactions, as they are arguably the most frequently used APIs for core workflow automation.

GlideRecord: Your Server-Side Database Commander

If you’re looking to automate tasks that involve manipulating data in your ServiceNow database, GlideRecord is your go-to API. It’s a server-side JavaScript class that provides an object-oriented way to query, insert, update, and delete records from tables. Forget about writing complex SQL; GlideRecord translates your JavaScript commands into efficient database operations, making your life as a developer much easier.

Important Note: Always test your GlideRecord scripts thoroughly on a non-production instance first! Incorrectly constructed queries or operations can lead to unintended data loss or corruption in your production environment.

Why is GlideRecord So Important?

  • Database Abstraction: You interact with records and fields using JavaScript objects, not raw SQL.
  • CRUD Operations: Perform all fundamental database operations (Create, Read, Update, Delete).
  • Server-Side Execution: Runs on the ServiceNow server, ensuring performance and security, often in Business Rules, Script Includes, or Scheduled Jobs.
  • Foundation for Automation: It’s the backbone for automating data-driven processes like updating task statuses, creating related records, or generating reports.

GlideRecord Architecture: Bridging JavaScript and the Database

Conceptually, GlideRecord acts as an intermediary:

JavaScript (Your Script) → GlideRecord Object → SQL Query (Generated by Platform) → Database

You write JavaScript; GlideRecord understands it and converts it into the necessary SQL to interact with the underlying database. This abstraction protects you from database specifics and potential SQL injection vulnerabilities.

Essential GlideRecord Methods: Your Toolkit

Let’s dive into some of the most common and powerful GlideRecord methods. Remember, these examples are best run in a ServiceNow instance’s “Scripts – Background” module for immediate execution and testing.

Tip: Using gs.print() vs. gs.info()
gs.print() writes directly to the output. gs.info() writes to the system log (and also the output if run in Scripts – Background). For quick debugging in Scripts – Background, either works well, but gs.info() is better practice for persistent logging in production scripts.

1. Basic Output and Arithmetic (gs.print() / gs.info())

Before diving into records, let’s confirm basic script execution:

gs.print('Welcome to ServiceNow Automation');
gs.info('The sum of 10 and 20 is: ' + (10 + 20));

Result: Prints the messages directly to the script output.

2. Querying All Records (new GlideRecord('table_name'), query(), next())

This is your fundamental method for retrieving records. You first instantiate a GlideRecord object for a specific table, then call query() to execute the retrieval, and finally iterate through the results using while(gr.next()).

var incidentGR = new GlideRecord('incident'); // Instantiate GlideRecord for the 'incident' table
incidentGR.query(); // Execute the query to retrieve all incidents
gs.info('All Incident Numbers:');
while (incidentGR.next()) { // Loop through each record found
    gs.print(incidentGR.number); // Access and print the 'number' field of the current record
}

Result: Lists the number of every incident record in your instance.

3. Filtering Records (addQuery(), addEncodedQuery(), addActiveQuery(), addInactiveQuery())

Filtering is crucial for targeted automation. You rarely want *all* records.

  • addQuery('field', 'value'): The most common way to add a simple equality condition.
  • addQuery('field', 'operator', 'value'): For more complex comparisons (e.g., less than, contains). Common operators include =, !=, >, <, >=, <=, STARTSWITH, ENDSWITH, CONTAINS, DOES NOT CONTAIN, IN, NOT IN.
  • addEncodedQuery('query_string'): Best for complex queries. You can build these by filtering a list view in ServiceNow, right-clicking the filter breadcrumbs, and selecting "Copy query."
  • addActiveQuery() / addInactiveQuery(): Convenient shortcuts for filtering by the active field.
// Exercise 1: Priority 1 Incidents
var p1Incidents = new GlideRecord('incident');
p1Incidents.addQuery('priority', 1); // Get incidents where priority is 1
p1Incidents.query();
gs.info('Priority 1 Incidents:');
while (p1Incidents.next()){
    gs.print(p1Incidents.number + ' - ' + p1Incidents.short_description);
}

// Exercise 2: Multiple Queries (AND condition by default)
var complexIncidents = new GlideRecord('incident');
complexIncidents.addQuery('active', true);
complexIncidents.addQuery('priority', 1);
complexIncidents.addQuery('category', 'software');
complexIncidents.query();
gs.info('\nActive, Priority 1, Software Incidents:');
while(complexIncidents.next()){
    gs.print(complexIncidents.number);
}

// Exercise 3 & 4: Using addEncodedQuery (more efficient for many conditions)
var encodedQueryString = 'active=true^category=software^priority=1'; // Copied from filter breadcrumbs
var encodedIncidents = new GlideRecord('incident');
encodedIncidents.addEncodedQuery(encodedQueryString);
encodedIncidents.query();
gs.info('\nIncidents via Encoded Query:');
while(encodedIncidents.next()){
    gs.print(encodedIncidents.number);
}

// Exercise 5: Using operators (e.g., <=, CONTAINS)
var filteredIncidents = new GlideRecord('incident');
filteredIncidents.addActiveQuery(); // active=true
filteredIncidents.addQuery('priority', '<=', 2); // Priority Critical (1) or High (2)
filteredIncidents.addQuery('short_description', 'CONTAINS', 'SAP'); // Short description contains 'SAP'
filteredIncidents.query();
gs.info('\nActive, Critical/High SAP-related Incidents:');
while(filteredIncidents.next()){
    gs.print(filteredIncidents.number + ' - ' + filteredIncidents.short_description);
}

// Exercise 8: Using 'IN' operator for multiple values
var categories = ['software', 'hardware'];
var categoryIncidents = new GlideRecord('incident');
categoryIncidents.addQuery('category', 'IN', categories);
categoryIncidents.query();
gs.info('\nSoftware/Hardware Category Incidents:');
while(categoryIncidents.next()) {
    gs.print(categoryIncidents.getValue('number') + ' - ' + categoryIncidents.getValue('category'));
}

4. Ordering and Limiting Results (orderBy(), orderByDesc(), setLimit())

When displaying or processing records, you often need them in a specific order or only a subset of the total.

// Exercise 12: Order by short description (ascending)
var orderedIncidents = new GlideRecord('incident');
orderedIncidents.addQuery('priority', 1);
orderedIncidents.addQuery('category', 'software');
orderedIncidents.orderBy('short_description'); // Order by a specific field
orderedIncidents.query();
gs.info('\nPriority 1 Software Incidents (Ordered by Short Description ASC):');
while(orderedIncidents.next()){
    gs.print(orderedIncidents.number + ' - ' + orderedIncidents.short_description);
}

// Exercise 12: Order by short description (descending)
var orderedDescIncidents = new GlideRecord('incident');
orderedDescIncidents.addQuery('priority', 1);
orderedDescIncidents.addQuery('category', 'software');
orderedDescIncidents.orderByDesc('short_description'); // Order by a specific field in descending order
orderedDescIncidents.query();
gs.info('\nPriority 1 Software Incidents (Ordered by Short Description DESC):');
while(orderedDescIncidents.next()){
    gs.print(orderedDescIncidents.number + ' - ' + orderedDescIncidents.short_description);
}

// Exercise 13: Limiting results
var limitedIncidents = new GlideRecord('incident');
limitedIncidents.addQuery('priority', 1);
limitedIncidents.orderByDesc('sys_created_on'); // Get the newest
limitedIncidents.setLimit(5); // Only retrieve the first 5 records
limitedIncidents.query();
gs.info('\nLatest 5 Priority 1 Incidents:');
while(limitedIncidents.next()){
    gs.print(limitedIncidents.number + ' - ' + limitedIncidents.short_description);
}

5. Retrieving Specific Records (get(), getUniqueValue())

Sometimes you need to fetch a single record based on its unique identifier (like sys_id or number).

// Exercise 14: Get a record by number
var specificIncident = new GlideRecord('incident');
if (specificIncident.get('number', 'INC0000001')) { // Returns true if record is found
    gs.print('\nFound Incident INC0000001. Sys_ID: ' + specificIncident.sys_id + ', Short Description: ' + specificIncident.short_description);
} else {
    gs.print('\nIncident INC0000001 not found.');
}

// Exercise 14 Example 2: Get a record by sys_id
// Replace with an actual sys_id from your instance for testing
var specificSysId = 'a5610d7a976311100f723900302b1f3c'; 
var incidentBySysId = new GlideRecord('incident');
if (incidentBySysId.get(specificSysId)) { // If only one argument, it assumes sys_id
    gs.print('\nFound Incident by Sys_ID. Number: ' + incidentBySysId.number + ', Short Description: ' + incidentBySysId.short_description);
} else {
    gs.print('\nIncident with sys_id ' + specificSysId + ' not found.');
}

// Exercise 20: Getting the unique value (sys_id)
var grUniq = new GlideRecord('incident');
grUniq.query();
if (grUniq.next()) {
    gs.print('\nUnique value (sys_id) of first incident: ' + grUniq.getUniqueValue());
}

6. Counting Records and Metadata (getRowCount(), getTableName(), isValid(), isValidField())

These methods help you understand the data you're working with or validate your scripting assumptions.

// Exercise 16: Get count of records
var activeUsers = new GlideRecord('sys_user');
activeUsers.addActiveQuery();
activeUsers.query();
gs.print('\nNumber of active users: ' + activeUsers.getRowCount());

// Exercise 17: Get table name
var grTableName = new GlideRecord('change_request');
gs.print('Current GlideRecord table name: ' + grTableName.getTableName());

// Exercise 27: Check if table exists
var existingTable = new GlideRecord('incident');
gs.print('Does "incident" table exist? ' + existingTable.isValid());
var nonExistentTable = new GlideRecord('uday_custom_table'); // Assuming this table doesn't exist
gs.print('Does "uday_custom_table" table exist? ' + nonExistentTable.isValid());

// Exercise 28: Check if field exists
var grField = new GlideRecord('incident');
gs.print('Does "incident" table have "category" field? ' + grField.isValidField('category'));
gs.print('Does "incident" table have "non_existent_field" field? ' + grField.isValidField('non_existent_field'));

7. Accessing Field Values (getValue(), getDisplayValue())

These methods are crucial for retrieving data from fields, with an important distinction.

  • getValue('field_name'): Returns the actual stored value (e.g., '1' for Critical priority).
  • getDisplayValue('field_name'): Returns the user-friendly display value (e.g., 'Critical' for priority 1).
// Exercise 18: Get actual field value
var incValue = new GlideRecord('incident');
incValue.addQuery('number', 'INC0000001'); // Replace with a valid incident number
incValue.query();
if (incValue.next()) {
    gs.print('\nIncident ' + incValue.number + ' Category (actual value): ' + incValue.getValue('category'));
}

// Exercise 19: Get display value
var incDisplayValue = new GlideRecord('incident');
incDisplayValue.addQuery('number', 'INC0000001'); // Replace with a valid incident number
incDisplayValue.query();
if (incDisplayValue.next()) {
    gs.print('Incident ' + incDisplayValue.number + ' Priority (display value): ' + incDisplayValue.priority.getDisplayValue());
}

8. Creating New Records (initialize(), setValue(), insert(), newRecord(), isNewRecord())

Automating record creation is a cornerstone of workflow automation.

  • initialize(): Prepares a new, empty record.
  • setValue('field_name', 'value'): Sets a field's value using its name (string).
  • gr.field_name = 'value': An alternative, more direct way to set field values (dot-walking).
  • insert(): Saves the new record to the database.
  • newRecord(): Initializes a new record and assigns a sys_id, skipping some default value population that initialize() does. Generally, initialize() is safer as it populates default values and handles field initialization better.
  • isNewRecord(): Checks if the current GlideRecord object represents a record not yet saved to the database.
// Exercise 25: Create a new incident using initialize() and direct field assignment
var newIncident = new GlideRecord('incident');
newIncident.initialize(); // Get ready to create a new record
newIncident.category = 'network'; // Set field values directly
newIncident.short_description = 'Automated: Firewall Issue Detected';
newIncident.priority = 1;
newIncident.insert(); // Save the new incident
gs.print('\nNew Incident Created (using initialize()): ' + newIncident.number);

// Exercise 22 & 23: Using setValue() and getElement()
var newIncident2 = new GlideRecord('incident');
newIncident2.initialize();
newIncident2.setValue('category', 'database'); // Using setValue()
newIncident2.setValue('short_description', 'Critical DB performance problem');
newIncident2.insert();
gs.print('New Incident Created (using setValue()): ' + newIncident2.number + ' - ' + newIncident2.getElement('short_description'));

// Exercise 26: isNewRecord() and newRecord()
var grNewRec = new GlideRecord('incident');
grNewRec.newRecord(); // Creates a new record and assigns a sys_id
gs.info('\nIs this a new record after newRecord()? ' + grNewRec.isNewRecord());
grNewRec.short_description = 'Testing newRecord() method';
grNewRec.insert();
gs.info('New record created: ' + grNewRec.number);
gs.info('Is this a new record after insert()? ' + grNewRec.isNewRecord()); // Should be false now

9. Updating Records (update(), updateMultiple())

Modifying existing data is central to automation.

  • update(): Saves changes to the *current* record after you've modified its field values.
  • updateMultiple(): Updates *all* records matching the current query with the specified field changes. Use with extreme caution!
// Exercise 34: Update a single record
var incidentToUpdate = new GlideRecord('incident');
if (incidentToUpdate.get('number', 'INC0000002')) { // Replace with an actual incident number
    gs.print('\nUpdating incident ' + incidentToUpdate.number + '. Old state: ' + incidentToUpdate.state.getDisplayValue());
    incidentToUpdate.setValue('state', 2); // Set state to 'In Progress'
    incidentToUpdate.update();
    gs.print('Incident ' + incidentToUpdate.number + ' updated. New state: ' + incidentToUpdate.state.getDisplayValue());
} else {
    gs.print('\nIncident INC0000002 not found for update.');
}

// Exercise 35: Update multiple records (use with extreme caution!)
// This example changes all incidents with category 'hardware' to 'software'.
// NEVER run this on production without careful planning and backup!
var incidentsToUpdateMultiple = new GlideRecord('incident');
incidentsToUpdateMultiple.addQuery('category', 'hardware');
incidentsToUpdateMultiple.setValue('category', 'software'); // Set the new value
// incidentsToUpdateMultiple.setWorkflow(false); // Optionally, disable Business Rules for this update
// incidentsToUpdateMultiple.autoSysFields(false); // Optionally, prevent sys_ fields from updating
// incidentsToUpdateMultiple.updateMultiple(); // Uncomment to run
// gs.print('\nAttempted to update multiple hardware incidents to software.');

10. Deleting Records (deleteRecord(), deleteMultiple())

Deleting records programmatically should always be done with the utmost care.

  • deleteRecord(): Deletes the *current* record.
  • deleteMultiple(): Deletes *all* records that satisfy the current query conditions. This is a very powerful and potentially dangerous method.
// Exercise 36: Delete a single record
var incidentToDelete = new GlideRecord('incident');
if (incidentToDelete.get('number', 'INC0010013')) { // Replace with a test incident number you're comfortable deleting
    gs.print('\nDeleting incident ' + incidentToDelete.number);
    incidentToDelete.deleteRecord();
    gs.print('Incident ' + incidentToDelete.number + ' deleted.');
} else {
    gs.print('\nIncident INC0010013 not found for deletion.');
}

// Exercise 37: Delete multiple records (USE WITH EXTREME CAUTION!)
// This example deletes all incidents with priority 4.
// NEVER run this on production without careful planning and backup!
var incidentsToDeleteMultiple = new GlideRecord('incident');
incidentsToDeleteMultiple.addQuery('priority', 4);
// incidentsToDeleteMultiple.query(); // You must query first before deleteMultiple in recent versions
// incidentsToDeleteMultiple.deleteMultiple(); // Uncomment to run
// gs.print('\nAttempted to delete multiple priority 4 incidents.');

11. Access Control (canCreate(), canRead(), canWrite(), canDelete())

These methods are useful for checking if the current user has the necessary permissions based on Access Control Lists (ACLs).

// Exercise 38-41: Check permissions
var grACL = new GlideRecord('incident');
gs.print('\nCan current user create incident? ' + grACL.canCreate());
gs.print('Can current user read incident? ' + grACL.canRead());
gs.print('Can current user write incident? ' + grACL.canWrite());
gs.print('Can current user delete incident? ' + grACL.canDelete());

12. Advanced Methods (autoSysFields(), setWorkflow(), addJoinQuery())

These methods offer finer control over how your scripts interact with the platform.

  • autoSysFields(false): Prevents system fields like sys_updated_on, sys_updated_by, sys_mod_count from being updated when a record is changed. Useful for data migrations or specific integrations where you want to preserve original system timestamps. (Note: Not recommended for general use, and might have limitations in scoped applications).
  • setWorkflow(false): Prevents Business Rules, Workflow activities, and Flow Designer flows from executing during a GlideRecord operation. Extremely powerful, but use with caution as it can bypass critical business logic.
  • addJoinQuery(): Allows you to join two tables and filter results based on conditions across both, similar to a SQL JOIN.
// Exercise 43: Updating without system fields or workflow (USE WITH CAUTION!)
var incidentsSilentUpdate = new GlideRecord('incident');
incidentsSilentUpdate.addQuery('state', 1); // Get all 'New' incidents
incidentsSilentUpdate.setLimit(2); // Limiting for safety in example
incidentsSilentUpdate.query();
gs.print('\nUpdating records without touching system fields or running workflows:');
while (incidentsSilentUpdate.next()) {
    incidentsSilentUpdate.autoSysFields(false); // Do not update system fields
    incidentsSilentUpdate.setWorkflow(false); // Do not run Business Rules/Workflows
    incidentsSilentUpdate.setValue('state', 2); // Set state to 'In Progress'
    incidentsSilentUpdate.update();
    gs.print('Incident ' + incidentsSilentUpdate.number + ' silently updated to In Progress.');
}

// Exercise 44: addJoinQuery - Find problems with associated incidents
var problemGR = new GlideRecord('problem');
// Join 'problem' with 'incident' where problem.opened_by matches incident.caller_id
// This is a simplified example; actual joins might be more complex based on relationships.
problemGR.addJoinQuery('incident', 'opened_by', 'caller_id');
problemGR.query();
gs.print('\nProblems with associated incidents (based on caller_id match):');
while(problemGR.next()){
    gs.print(problemGR.number + ' - ' + problemGR.short_description);
}

Troubleshooting GlideRecord Scripts

  • Incorrect Table/Field Names: A common culprit. Double-check your table (e.g., incident, sys_user) and field names (e.g., short_description, state). Case sensitivity matters for field names.
  • No Records Returned: Your query might be too restrictive or the data doesn't exist. Use gs.print(gr.getEncodedQuery()) to see the exact query generated, then paste it into a list view to verify results.
  • Infinite Loops: If your script uses a while(gr.next()) loop and hangs, ensure your query is finite and the condition eventually becomes false.
  • ACL Issues: If your script runs under a user context (e.g., a specific integration user), it might not have permission to read/write certain records. Test with an admin user, then verify ACLs.
  • Performance: Large queries or queries inside loops can kill performance. Use setLimit(), addEncodedQuery(), and optimize your logic. Avoid query() without conditions if possible.
  • Missing next(): For read operations, always remember to call gr.next() inside your loop to advance to the next record.
  • Missing update()/insert(): After modifying field values, you *must* call gr.update() or gr.insert() to save changes to the database.

GlideRecord Interview Relevance

Expect questions like:

  • "Explain GlideRecord and its primary purpose."
  • "When would you use addQuery() versus addEncodedQuery()?"
  • "What's the difference between get() and query()?"
  • "How do you create a new record using GlideRecord?"
  • "When would you use setWorkflow(false) or autoSysFields(false), and what are the implications?"
  • "How do you handle performance considerations with GlideRecord when dealing with large datasets?"
  • "What happens if you forget gr.next() in a while loop?"

GlideForm (g_form): Your Client-Side UI Orchestrator

While GlideRecord handles data on the server, GlideForm is your essential tool for manipulating the user interface on the client-side (directly in the user's browser). The global object g_form gives you access to a rich set of methods that allow you to dynamically control form fields, sections, and messages based on user interactions or other conditions.

Why is GlideForm (g_form) So Important?

  • Dynamic UI: Hide/show fields, make them mandatory/read-only based on real-time user input.
  • Enhanced User Experience: Provide immediate feedback, guide users through forms, and simplify data entry.
  • Client-Side Validation: Perform validations before the form is submitted to the server, reducing server load and improving responsiveness.
  • Used in Client Scripts: g_form methods are primarily used within Client Scripts (onLoad, onChange, onSubmit) and Catalog Client Scripts.
Important Note: While g_form offers powerful UI control, always consider if a UI Policy can achieve the same result. UI Policies are often easier to maintain and require less code, adhering to ServiceNow's "low-code/no-code" philosophy where appropriate. Use g_form for more complex, dynamic, or script-dependent scenarios.

Essential GlideForm Methods: Your UI Toolkit

To experiment with these, open any record in ServiceNow, then open your browser's developer console (usually Ctrl+Shift+J or F12) and type your commands into the JavaScript console.

1. Getting and Setting Field Values (getValue(), setValue())

These are fundamental for reading and writing data on the form.

// Exercise 1: Get specific field value
alert(g_form.getValue('category')); // Displays the current category value in a browser alert

// Exercise 2: Set specific field value
// After running getValue(), try setting it.
g_form.setValue('category', 'hardware'); // Sets the category field to 'hardware'
alert(g_form.getValue('category')); // Confirm the change

Practical Application: In an onChange client script, if a user selects 'High' priority, you might automatically set the 'Category' to 'Critical Incident' using g_form.setValue().

2. Controlling Field Properties (setDisabled(), setMandatory(), setDisplay(), setVisible())

These methods allow you to dynamically change how fields appear and behave on the form.

  • setDisabled('field_name', true/false): Makes a field read-only or editable.
  • setMandatory('field_name', true/false): Makes a field required or optional.
  • setDisplay('field_name', true/false): Hides or shows a field, removing its space from the form layout.
  • setVisible('field_name', true/false): Hides or shows a field, but *maintains* its allocated space on the form layout (less common).
// Exercise 3: Make a field read-only
g_form.setDisabled('category', true); // Category field becomes uneditable
// Note: Often better achieved with UI Policy.

// Exercise 4: Make a field mandatory
g_form.setMandatory('short_description', true); // Short Description becomes mandatory
// Note: Often better achieved with UI Policy.

// Exercise 5: Hide/Unhide a field (removes space)
g_form.setDisplay('business_service', false); // Hides Business Service field and collapses its space

// Exercise 6: Hide/Unhide a field (maintains space)
g_form.setVisible('subcategory', false); // Hides Subcategory field but leaves an empty space where it was

Practical Application: If a 'VIP User' checkbox is checked, you might make the 'Approver' field mandatory and display a special 'VIP Approval Process' section.

3. Displaying Messages (addInfoMessage(), addErrorMessage(), showFieldMsg(), clearMessages())

Communicating with the user directly on the form is crucial for a good experience.

g_form.addInfoMessage('This incident is currently being reviewed by a specialist.');
g_form.addErrorMessage('Please ensure all mandatory fields are filled before submission.');
g_form.showFieldMsg('short_description', 'Please provide a detailed description.', 'error'); // Field-specific message
g_form.clearMessages(); // Clears all info and error messages from the top of the form

Practical Application: On onSubmit, if a custom validation fails, you can use g_form.addErrorMessage() to tell the user what went wrong, and prevent submission.

4. Form Actions (save(), submit())

Programmatically save or submit the current form.

// g_form.save(); // Saves the current record without submitting
// g_form.submit(); // Submits the current form (triggers onSubmit client scripts, Business Rules)

Practical Application: After a user confirms an action in a custom dialog, you might automatically save the record.

5. Form Metadata (getTableName(), getUniqueValue(), isNewRecord())

Accessing information about the current form and record.

alert(g_form.getTableName()); // Returns 'incident' if on an incident form
alert(g_form.getUniqueValue()); // Returns the sys_id of the current record
alert(g_form.isNewRecord()); // Returns true if it's a new record, false if existing

Troubleshooting GlideForm Scripts

  • Caching Issues: Client Scripts are cached by the browser. After making changes, clear your browser cache (Ctrl+F5 or Shift+F5) or use an incognito window to ensure you're seeing the latest version.
  • Incorrect Field Names: Similar to GlideRecord, misspelled field names will lead to errors. Use the field's 'Name' (backend name) not its 'Label'.
  • Script Order: Client Scripts execute in a specific order. If one script relies on another, ensure their execution order is correct (controlled by the 'Order' field).
  • UI Policy Conflicts: If a g_form script isn't working, a conflicting UI Policy might be overriding it. Check for UI Policies on the same table/view. UI Policies generally take precedence or can interfere.
  • Browser Console Errors: Always check your browser's developer console for JavaScript errors. These provide invaluable clues.
  • Asynchronous Operations: If your client script makes a GlideAjax call, remember that it's asynchronous. Don't expect immediate results; handle the response in the callback function.

GlideForm Interview Relevance

Interviewers might ask:

  • "What is g_form, and where is it primarily used?"
  • "When would you use g_form.setValue() versus a UI Policy?"
  • "How do you make a field mandatory on the client-side?"
  • "Explain the difference between setDisplay() and setVisible()."
  • "How do you alert the user to a validation error without submitting the form?"
  • "What are some common issues you face with Client Scripts and how do you troubleshoot them?"

Bringing it All Together: Workflow Automation in Action

GlideRecord and GlideForm are not just isolated APIs; they are often used in conjunction to build robust workflow automations. Imagine a scenario:

  • A user changes the state of an incident to "Resolved" (client-side interaction using g_form or a UI Action).
  • An onSubmit Client Script might validate if all resolution notes are provided using g_form.getValue() and g_form.addErrorMessage().
  • Upon successful submission, a server-side Business Rule triggers.
  • This Business Rule uses GlideRecord to create a related "Problem" record (new GlideRecord('problem'); prob.initialize(); prob.insert();) or update an existing one.
  • It might then query other related incidents (incidentGR.addQuery('parent_incident', current.sys_id); incidentGR.query();) and close them out automatically (incidentGR.setValue('state', 7); incidentGR.update();).

This seamless interaction between client-side user experience and server-side data manipulation is the essence of powerful ServiceNow workflow automation.

General ServiceNow Scripting Best Practices & Troubleshooting

  • Adhere to Coding Standards: Use consistent naming conventions, comments, and proper indentation.
  • Utilize Script Includes: Avoid large blocks of code in Business Rules or Client Scripts. Encapsulate reusable functions in Script Includes.
  • Minimize Client-Side Scripting: Only use Client Scripts when absolutely necessary for UI/UX. Server-side logic is generally more secure and performant.
  • Test Incrementally: Don't write a huge script and expect it to work perfectly. Test small pieces as you go.
  • Use gs.info() / gs.debug(): For server-side debugging, log messages extensively. Remove or disable them in production.
  • Consider Performance: Be mindful of how many records you query or update, especially in loops. Optimize queries.
  • Security First: Always consider ACLs and user roles. Never trust client-side data for critical security decisions.
  • Stay Updated: ServiceNow constantly evolves. Keep an eye on release notes for new features, deprecations, and best practice changes.

Conclusion

Workflow automation in ServiceNow, while supported by fantastic low-code tools like Flow Designer, truly shines when you harness the power of scripting. The Glide API, with its foundational components like GlideRecord and GlideForm, empowers developers to build sophisticated, highly customized solutions that meet unique business challenges.

By mastering these APIs, you gain the ability to precisely control data, enhance user experiences, and integrate seamlessly across your enterprise. Remember, practice is key. Keep experimenting in your non-production instances, learn from the official ServiceNow documentation, and engage with the vibrant ServiceNow developer community. Your journey to becoming a ServiceNow automation maestro starts here!

© 2023 [Your Name/Company Name, if applicable]. All rights reserved.


Scroll to Top