Dynamically Stop Form Submission: A Comprehensive Guide






Mastering Dynamic Form Submission Control in ServiceNow


Mastering Dynamic Form Submission Control in ServiceNow

Ever found yourself wrestling with forms in ServiceNow, needing to stop a user dead in their tracks before they submit a potentially problematic record? It’s a common scenario. Whether it’s ensuring all associated tasks are closed before an incident is resolved, or enforcing specific business logic based on a combination of field values, controlling form submission dynamically is a critical skill for any ServiceNow developer. This article will dive deep into the techniques that empower you to create smarter, more robust forms.

We’ll explore how to leverage ServiceNow’s powerful scripting and configuration capabilities to intercept submissions, validate data, and guide users towards correct data entry. Think of it as building intelligent gatekeepers for your data, ensuring its integrity and adherence to your organization’s processes.

The Core Concept: Preventing Unwanted Submissions

At its heart, stopping form submission dynamically means intercepting the default ‘submit’ action and conditionally preventing it from completing. This is typically achieved by:

  • Client-side validation: Using JavaScript to check conditions on the form before the data even gets sent to the server. This provides immediate feedback to the user.
  • Server-side validation: Using business rules or other server-side scripts to perform checks after the data has been submitted but before it’s saved to the database.

The key ServiceNow mechanism we’ll be focusing on for client-side prevention is the current.setAbortAction(true) method. When this is called within a script that runs in the right context (like a before business rule or an onSubmit client script), it tells ServiceNow to halt the current operation – in this case, the form submission.

Leveraging Business Rules for Server-Side Intervention

Business rules are the workhorses of server-side logic in ServiceNow. For stopping form submissions, we’ll primarily use ‘Before’ Business Rules. These rules execute on the server *before* a record is inserted or updated into the database, making them ideal for comprehensive validation.

Scenario: Incident and Task Dependency

A classic example, referenced in our guide, is preventing an incident from being closed if its associated incident tasks are still open. This ensures that all necessary work related to the incident is completed before it’s marked as resolved.

Let’s break down how to implement this using a ‘Before’ Business Rule:

Steps to Implement

  1. Navigate to System Definition > Business Rules.
  2. Click New.
  3. Name: Give it a descriptive name, e.g., “Prevent Incident Closure with Open Tasks”.
  4. Table: Select ‘Incident’ [incident].
  5. When to run:
    • When: Select ‘Before’.
    • Operation: Select ‘Update’ and ‘Insert’. (While you might only think of closure for ‘Update’, it’s good practice to consider ‘Insert’ if there are any edge cases where an incident might be ‘closed’ upon creation, though less common).
  6. Advanced: Check this box to enable the script field.
  7. Script: Paste the following script:

    (function executeRule(current, previous /*null when async*/) {

        // Only apply this check when the incident is being moved to a closed state
        // You'll need to determine the exact 'value' for your 'Closed' state.
        // Common values are 7 for 'Closed' or 3 for 'Resolved', depending on your instance configuration.
        // Always verify the state value in your system.
        var closedStateValue = 7; // Example: Assuming 7 is the sys_id for 'Closed' state

        if (current.state != previous.state && current.state == closedStateValue) {
            // Check for open Incident Tasks
            var grTask = new GlideRecord('incident_task');
            grTask.addQuery('incident', current.sys_id);
            // Assuming 'state' = 3 means 'Closed' for incident tasks. Adjust if necessary.
            grTask.addQuery('state', '!=', 3);
            grTask.query();

            if (grTask.hasNext()) {
                gs.addErrorMessage('Cannot close the incident because there are open incident tasks. Please close all related tasks first.');
                current.setAbortAction(true);
                return; // Exit the script
            }

            // Add similar checks for Problem and Change Request if needed, by querying their respective task tables.
            // Example for Problem Tasks:
            /*
            var grProblemTask = new GlideRecord('problem_task');
            grProblemTask.addQuery('problem', current.sys_id); // Assuming incident table sys_id is stored in 'problem' field of problem_task
            grProblemTask.addQuery('state', '!=', 3); // Adjust state value for Problem Task closure
            grProblemTask.query();
            if (grProblemTask.hasNext()) {
                gs.addErrorMessage('Cannot close the incident because there are open problem tasks.');
                current.setAbortAction(true);
                return;
            }
            */

            // Example for Change Request Tasks:
            /*
            var grChangeTask = new GlideRecord('change_task');
            grChangeTask.addQuery('change_request', current.sys_id); // Assuming incident table sys_id is stored in 'change_request' field of change_task
            grChangeTask.addQuery('state', '!=', 3); // Adjust state value for Change Task closure
            grChangeTask.query();
            if (grChangeTask.hasNext()) {
                gs.addErrorMessage('Cannot close the incident because there are open change tasks.');
                current.setAbortAction(true);
                return;
            }
            */
        }

    })(current, previous);
    

Explanation of the Script:

  • The script first checks if the state of the incident is actually changing to a ‘Closed’ state. This prevents the script from running unnecessarily on every update.
  • It then queries the incident_task table, linking tasks to the current incident (incident=current.sys_id).
  • It filters for tasks where the state is *not* ‘Closed’ (state != 3). Important: You *must* verify the actual numerical value for your ‘Closed’ state in your ServiceNow instance for both incident and task tables. This can be found by right-clicking the state field label, selecting “Configure Dictionary,” and looking at the “Choice List Specification.”
  • If any open tasks are found (grTask.hasNext()), an error message is displayed to the user using gs.addErrorMessage().
  • Crucially, current.setAbortAction(true) is called, preventing the incident from being saved (i.e., closed).
  • The commented-out sections show how you would extend this logic to include checks for associated problem tasks and change tasks if your incident management process involves them.

Troubleshooting Tip: State Values are Key!

The most common pitfall with this type of business rule is using the incorrect state values. Always confirm the exact numerical sys_id for your ‘Closed’ (or ‘Resolved’) states in both the parent table (e.g., Incident) and the related task tables (e.g., Incident Task, Problem Task, Change Task). You can find these by inspecting the dictionary entries for the ‘state’ field.

Interview Relevance:

This scenario is a goldmine for interviews. Be prepared to explain: the difference between client-side and server-side validation, why a ‘Before’ Business Rule is suitable here, the role of current.setAbortAction(true), and how to query related records. Understanding how to handle state values correctly is also a major plus.

Client-Side Control with UI Policies and Client Scripts

While Business Rules are powerful for server-side enforcement, client-side scripting offers immediate feedback to users, improving the user experience. UI Policies and Client Scripts are your go-to tools here.

UI Policies: The Declarative Approach

UI Policies are a no-code/low-code way to control form behavior, including making fields mandatory, read-only, or visible. They are excellent for simpler, condition-based actions.

Key UI Policy Concepts:

  • Run Scripts: Allows you to execute custom JavaScript when the UI Policy conditions are met. This is where you can implement logic to dynamically stop submissions.
  • On Load: Determines if the UI Policy should run when the form initially loads.
  • Reverse if False: If checked, the UI Policy’s actions are reversed when the condition becomes false.
  • Global: If checked, the UI Policy applies to all views. If unchecked, you can specify specific views.
  • Inherit: Applies the UI Policy to child tables that extend the current table.

Can you stop a form submission directly with a UI Policy? Not directly in the same way as setAbortAction(true). UI Policies are primarily for manipulating field attributes (mandatory, read-only, visible) and executing scripts. To *stop* a submission, you’d typically use the ‘Run Scripts’ feature within a UI Policy to call an onSubmit client script or directly implement the abort logic there.

Client Scripts: For Dynamic Interactions

Client Scripts are where the real JavaScript magic happens on the client side. They are essential for complex, dynamic form interactions.

The onSubmit Client Script

This is the most direct way to intercept form submissions from the client side. An onSubmit client script runs *just before* the form data is sent to the server.

How it Works:

Within an onSubmit client script, you write JavaScript code. If your script returns false, the form submission is cancelled. This is the client-side equivalent of setAbortAction(true).

Scenario: Preventing Submission if a Field is Empty

Let’s say you have a field, like ‘Assignment Group’, and you don’t want the form to submit if it’s empty, unless a specific ‘Caller’ is selected. This requires dynamic logic that can only be handled by a client script.

Steps to Implement
  1. Navigate to System Definition > Client Scripts.
  2. Click New.
  3. Name: e.g., “Prevent Submission Without Assignment Group”.
  4. Table: Select the relevant table (e.g., ‘Incident’ [incident]).
  5. UI Type: Select ‘Desktop’ (or ‘All’ if applicable).
  6. On Load: Leave unchecked.
  7. On Change: Leave unchecked.
  8. On Submit: Check this box.
  9. Script: Paste the following code:

    function onSubmit() {
        // Get the value of the 'Assignment Group' field
        var assignmentGroup = g_form.getValue('assignment_group');
        // Get the value of the 'Caller' field
        var caller = g_form.getValue('caller_id'); // Assuming 'caller_id' is the field name for caller

        // Define a condition where submission is allowed even if Assignment Group is empty
        // For example, if a specific caller is selected, or if a certain 'Urgency' is set.
        // Let's say, for this example, if the 'Caller' is "John Doe" (replace with actual caller sys_id or name if needed),
        // or if the urgency is low, submission is allowed even without an assignment group.
        // This is a placeholder; your actual logic will vary.

        // Example: If caller is NOT 'System Administrator' AND assignment group is empty, abort.
        var adminSysID = '68187483db21100015b4a1b948961926'; // Replace with actual sys_id of System Administrator group or user
        var isCallerAdmin = (caller == adminSysID); // Adjust this check based on how you identify admin callers

        if (!isCallerAdmin && assignmentGroup == '') {
            // If the caller is not an admin and the assignment group is empty,
            // display an error message and prevent submission.
            g_form.addErrorMessage('An Assignment Group must be selected to submit this record.');
            return false; // Returning false cancels the form submission
        }

        // If the conditions are met, the function implicitly returns true (or you can add 'return true;')
        // and the form will submit.
        return true;
    }
    

Important Notes on Client Scripts:

  • g_form.getValue('field_name'): Retrieves the value of a field.
  • g_form.addErrorMessage('Your message'): Displays a red error message at the top of the form.
  • return false;: This is the critical part that stops the submission.
  • return true;: Allows the submission to proceed.
  • Ensure you use the correct field names (e.g., assignment_group, caller_id) as they appear in the dictionary.

Troubleshooting Client Scripts:

Scripts not running? Double-check the ‘Table’ and ‘UI Type’ selected for the client script. Ensure the ‘On Submit’ checkbox is ticked. Open your browser’s developer console (usually F12) and look for JavaScript errors.

Error messages not appearing? Make sure you are using g_form.addErrorMessage() and not gs.addErrorMessage() (which is for server-side). Verify the logic that triggers the error.

Submission still happening? Ensure your script explicitly returns false when you want to block submission. If the script finishes without returning `false`, submission will proceed.

Reference Qualifiers: Refining Reference Field Data

While not directly stopping form submission, Reference Qualifiers play a crucial role in ensuring users select valid data *before* submission. By limiting the choices in reference fields, you prevent common data entry errors that might otherwise lead to submission issues or require correction later.

What are Reference Qualifiers?

Reference Qualifiers are filters applied to reference and list type fields. They restrict the records that can be selected in a lookup, ensuring only relevant and appropriate data is chosen.

Types of Reference Qualifiers:

  1. Simple Reference Qualifier:

    This is the most straightforward type. You define a fixed query using simple conditions directly in the dictionary entry of the reference field.

    Example: On an Incident form, if you have a ‘Business Service’ reference field, you might want to show only active Business Services. You’d set the qualifier to active=true.

    How to use: In the Reference field’s Dictionary Entry, go to the “Reference Qualifier” related list and add conditions like active=true or state=1.

  2. Dynamic Reference Qualifier:

    These qualifiers use pre-defined “Dynamic Filter Options” to create queries that can adapt based on context, like the current user or other form values. This is more flexible than simple qualifiers.

    Example: Displaying only incidents assigned to the current user’s assignment group. You’d create a Dynamic Filter Option for this and then select it in the Reference Qualifier.

    How to use: Create a Dynamic Filter Option via System Definition > Dynamic Filter Options. Then, in the Reference field’s Dictionary Entry, select “Dynamic” for the Type and choose your created Dynamic Filter Option.

  3. Advanced Reference Qualifier (JavaScript):

    This is the most powerful type, allowing you to write custom JavaScript code to define complex filtering logic. This is where you can incorporate multiple conditions, check related records, and leverage form context.

    Example: Filtering a ‘Configuration Item’ field to show only CIs that are part of a specific ‘CI Group’ and are currently in an ‘Operational’ state.

    How to use: In the Reference field’s Dictionary Entry, select “Advanced” for the Type and enter your JavaScript code in the “Reference qual” field. The code typically returns a string representing the encoded query. For example: javascript: 'category=' + g_form.getValue('category') + '^active=true^state=operational';

Interview Relevance:

Understanding reference qualifiers is a must. Interviewers will likely ask about the differences between simple, dynamic, and advanced. Be ready to provide practical examples of when you’d use each and how they contribute to data integrity.

Other Considerations for Data Integrity

Beyond stopping submissions, other ServiceNow features contribute to data quality:

UI Policies vs. Data Policies

  • UI Policies: Operate client-side (browser). They affect what the user sees and interacts with on the form (mandatory, read-only, visible). They are ideal for immediate user feedback.
  • Data Policies: Operate on both client and server sides. They enforce data consistency regardless of how the data is entered (form, import set, integration). They are crucial for data integrity across all entry points.

Can you convert UI Policies to Data Policies? Yes, ServiceNow provides a convenient “Convert to Data Policy” button on UI Policies that are suitable for conversion. However, there are cases where a UI Policy cannot be converted:

  • When you are controlling data visibility (making fields visible/hidden).
  • When you are controlling views.
  • When you are controlling related lists.
  • When the UI Policy primarily executes complex scripts for actions other than setting mandatory/read-only/visible.

Making Fields Mandatory/Read-Only

As mentioned in the reference, there are multiple ways to enforce mandatory or read-only fields:

  • Dictionary Properties: The fundamental settings for a field.
  • Dictionary Overrides: To change dictionary settings for extended tables.
  • UI Policies: For client-side conditional control.
  • Data Policies: For client and server-side conditional control.
  • g_form.setMandatory('field_name', true/false): In client scripts for dynamic control.
  • g_form.setReadOnly('field_name', true/false): In client scripts for dynamic control.

Putting It All Together: A Holistic Approach

Stopping form submissions dynamically isn’t about a single magical solution. It’s about strategically combining the right tools for the job:

  • For immediate user feedback and interactive validation: Use onSubmit Client Scripts and UI Policies with “Run Scripts”.
  • For critical data integrity checks that must happen regardless of user interaction or on the server: Use ‘Before’ Business Rules.
  • To ensure users can only select valid reference data: Implement appropriate Reference Qualifiers (Simple, Dynamic, or Advanced).
  • To enforce data standards across all entry points: Leverage Data Policies.

By mastering these techniques, you can build ServiceNow applications that are not only functional but also highly robust, ensuring data accuracy and streamlining your organization’s workflows. Remember to always test your logic thoroughly, especially when dealing with critical business processes.


Scroll to Top