GlideModal: Calling UI Pages with ServiceNow’s Modal API






Mastering GlideModal: A Practical Guide to Calling UI Pages in ServiceNow


Mastering GlideModal: A Practical Guide to Calling UI Pages in ServiceNow

In the dynamic world of ServiceNow development, creating engaging and user-friendly interfaces is paramount. While standard forms and lists are powerful, there are often scenarios where a more interactive, focused experience is needed. This is where GlideModal, ServiceNow’s built-in JavaScript API for displaying modal windows, shines. It allows us to seamlessly integrate custom UI Pages into our workflows, providing dynamic content and capturing user input without navigating away from the current page.

If you’re a ServiceNow administrator, developer, or aspiring professional, understanding how to effectively leverage GlideModal is a crucial skill. This article will guide you through the process of calling UI Pages with GlideModal, offering practical explanations, real-world examples, troubleshooting advice, and insights into its relevance in interviews.

Why Use GlideModal for UI Pages?

Before diving into the “how,” let’s briefly touch on the “why.” UI Pages in ServiceNow are essentially self-contained HTML pages with embedded Jelly, JavaScript, and CSS. They are incredibly flexible and can be used for a wide range of purposes, from simple confirmation dialogs to complex wizards and custom data entry forms.

GlideModal acts as the bridge, allowing you to invoke these UI Pages in a modal window. This approach offers several advantages:

  • Improved User Experience: Modals provide a focused environment for tasks, minimizing distractions and guiding users through specific processes.
  • Context Preservation: Users can remain on their current record or page while interacting with the modal, preserving their context.
  • Dynamic Content: UI Pages can be dynamically generated, meaning the content of your modal can change based on the current user, record, or other business logic.
  • Data Interaction: GlideModal allows for the passing of parameters to the UI Page and the retrieval of results, enabling two-way communication.
  • Streamlined Workflows: Complex tasks can be broken down into smaller, manageable steps presented within modals, making them less daunting for end-users.

The Fundamentals: Understanding GlideModal

GlideModal is a client-side JavaScript API. This means it’s primarily used within client scripts, UI actions, UI policies, and other client-side scripts. The core function we’ll be focusing on is GlideModal.open().

The basic syntax looks like this:

GlideModal.open({
        title: 'Your Modal Title',
        contentURL: 'your_ui_page.do?sysparm_id={sys_id_of_record}',
        onClose: function(result) {
            // Callback function when the modal is closed
            // 'result' might contain data passed back from the UI Page
        }
    });

Let’s break down the key parameters:

  • title: A string representing the title that will appear in the modal’s header.
  • contentURL: This is the most important parameter. It’s a URL that points to the UI Page you want to display. You can pass system parameters (like sysparm_id) to provide context to your UI Page.
  • onClose: A JavaScript function that executes when the modal is closed. This is where you’ll often handle any data returned from the UI Page.

Calling a Simple UI Page: A Step-by-Step Example

Let’s create a practical scenario. Imagine you have a button on the Incident form that, when clicked, opens a modal displaying a simple “Thank You” message and prompting the user to confirm an action. We’ll need two components: the UI Page and a UI Action to trigger it.

Step 1: Create the UI Page

Navigate to System UI > UI Pages and click New.

  • Name: simple_thank_you_modal
  • HTML:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide:util">
    <g:evaluate var="jvar_message" expression="RP.getWindowProperties().get('message')" />
    <div>
        <h3>${jvar_message}</h3>
        <p>Thank you for taking this action!</p>
        <button class="btn btn-primary" onclick="closeModal('success')">OK</button>
        <button class="btn btn-default" onclick="closeModal('cancel')">Cancel</button>
    </div>
    <script>
        function closeModal(status) {
            GlideModal.get().destroy({
                result: status // Pass back a status
            });
        }
    </script>
</j:jelly>
  • Client script: (This section is optional for this basic example, but good to know where it goes.)

Explanation of the UI Page HTML:

  • <?xml version="1.0" encoding="utf-8" ?> and <j:jelly ...>: Standard Jelly and ServiceNow XML declarations.
  • <g:evaluate var="jvar_message" expression="RP.getWindowProperties().get('message')" />: This is crucial for receiving data passed from the calling script. RP.getWindowProperties() accesses properties set when the modal was opened, and .get('message') retrieves the value associated with the key ‘message’.
  • <h3>${jvar_message}</h3>: Displays the message passed into the UI Page.
  • <button class="btn btn-primary" onclick="closeModal('success')">OK</button> and <button class="btn btn-default" onclick="closeModal('cancel')">Cancel</button>: These buttons call a JavaScript function closeModal(), passing a status.
  • <script> function closeModal(status) { GlideModal.get().destroy({ result: status }); } </script>: This embedded script defines the closeModal function. GlideModal.get() retrieves the current modal instance, and .destroy({ result: status }) closes the modal and optionally passes a result back to the calling script.

Step 2: Create the UI Action

Navigate to System UI > UI Actions and click New.

  • Name: Open Thank You Modal
  • Table: Incident [incident]
  • Action name: open_thank_you_modal
  • Form button: Check this box.
  • Client: Check this box.
  • Onclick: openMyModal();
  • Script:
function openMyModal() {
        var gdw = new GlideModal({
            title: 'Important Message',
            contentURL: 'simple_thank_you_modal.do?message=' + encodeURIComponent('Welcome to the Incident Form!'),
            onClose: function(result) {
                if (result === 'success') {
                    alert('You confirmed the action!');
                } else {
                    alert('You cancelled the action.');
                }
            }
        });
        gdw.render(); // Render the modal
    }

Explanation of the UI Action Script:

  • function openMyModal() { ... }: Defines the function that will be called by the onclick event.
  • var gdw = new GlideModal({...});: This is the modern, recommended way to instantiate GlideModal. It’s an object-oriented approach.
  • title: 'Important Message': Sets the modal’s title.
  • contentURL: 'simple_thank_you_modal.do?message=' + encodeURIComponent('Welcome to the Incident Form!'):
    • simple_thank_you_modal.do: This is the URL to our UI Page. ServiceNow automatically appends necessary parameters like sysparm_processor.
    • ?message=' + encodeURIComponent('Welcome to the Incident Form!'): Here, we are passing a parameter named message to our UI Page. encodeURIComponent is important to handle special characters in the message. This message parameter will be accessible in the UI Page using RP.getWindowProperties().get('message').
  • onClose: function(result) { ... }: This is the callback function. The result parameter will contain whatever was passed back by the UI Page’s destroy() method (in our case, ‘success’ or ‘cancel’).
  • gdw.render();: This method actually displays the modal.

Now, when you navigate to an Incident record and click the “Open Thank You Modal” button, you’ll see the modal pop up. Clicking “OK” will trigger the success alert, and clicking “Cancel” will trigger the cancel alert.

Passing Record-Specific Data to the UI Page

A common requirement is to display information from the current record within the modal. We can achieve this by passing the record’s sys_id or other field values as URL parameters.

Updating the UI Action

Let’s modify the UI Action script to pass the incident’s number and sys_id.

function openMyModal() {
        // Get the current record's sys_id and number
        var incidentSysId = g_form.getUniqueValue();
        var incidentNumber = g_form.getValue('number');

        var gdw = new GlideModal({
            title: 'Incident Details: ' + incidentNumber,
            contentURL: 'simple_thank_you_modal.do?sysparm_id=' + incidentSysId + '&incident_number=' + encodeURIComponent(incidentNumber) + '&message=' + encodeURIComponent('Details for incident ' + incidentNumber),
            onClose: function(result) {
                if (result === 'success') {
                    alert('Action confirmed for Incident ' + incidentNumber + '!');
                } else {
                    alert('Action cancelled for Incident ' + incidentNumber + '.');
                }
            }
        });
        gdw.render();
    }

Updating the UI Page to Receive Data

Now, let’s modify the UI Page’s HTML to access and display these new parameters:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide:util">
    <g:evaluate var="jvar_message" expression="RP.getWindowProperties().get('message')" />
    <g:evaluate var="jvar_incident_sys_id" expression="RP.getWindowProperties().get('sysparm_id')" />
    <g:evaluate var="jvar_incident_number" expression="RP.getWindowProperties().get('incident_number')" />

    <div>
        <h3>${jvar_message}</h3>
        <p>This modal is associated with Incident: <strong>${jvar_incident_number}</strong></p>
        <p>Incident Sys ID: <code>${jvar_incident_sys_id}</code></p>

        <button class="btn btn-primary" onclick="closeModal('success', '${jvar_incident_number}')">Confirm</button>
        <button class="btn btn-default" onclick="closeModal('cancel', '${jvar_incident_number}')">Cancel</button>
    </div>

    <script>
        function closeModal(status, incidentNum) {
            GlideModal.get().destroy({
                result: status, // Pass back the status
                incident: incidentNum // Pass back the incident number as well
            });
        }
    </script>
</j:jelly>

Key changes in the UI Page:

  • We now use RP.getWindowProperties().get('sysparm_id') and RP.getWindowProperties().get('incident_number') to retrieve the passed parameters.
  • The displayed message and incident details are dynamically updated.
  • The closeModal function now also passes back the incidentNum along with the status in the result object. This demonstrates how you can return multiple pieces of data.

Key changes in the UI Action:

  • g_form.getUniqueValue() fetches the sys_id of the current record.
  • g_form.getValue('number') fetches the value of the ‘number’ field.
  • These values are appended to the contentURL as sysparm_id and incident_number. Notice the use of &amp; which is the XML-encoded equivalent of & required within Jelly.
  • The onClose callback now also receives the incident number passed back from the modal.

Retrieving Data from the Modal and Performing Actions

Often, you want to collect input from the user within the modal and then use that data on the parent form. This requires careful handling of the data passed back from the UI Page.

Let’s create a scenario where we want to ask the user for a reason for closure when closing an incident.

Step 1: Create a New UI Page for Closure Reason

Name: incident_closure_reason_modal

HTML:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide:util">
    <g:evaluate var="jvar_incident_sys_id" expression="RP.getWindowProperties().get('sysparm_id')" />
    <g:evaluate var="jvar_incident_number" expression="RP.getWindowProperties().get('incident_number')" />

    <div class="container">
        <h3>Provide Closure Reason for ${jvar_incident_number}</h3>
        <div class="form-group">
            <label for="closure_reason">Reason for Closure:</label>
            <textarea id="closure_reason" class="form-control" rows="4"></textarea>
        </div>

        <div class="modal-footer">
            <button class="btn btn-primary" onclick="submitClosureReason()">Submit</button>
            <button class="btn btn-default" onclick="GlideModal.get().destroy()">Cancel</button>
        </div>
    </div>

    <script>
        function submitClosureReason() {
            var reason = gel('closure_reason').value;
            if (reason.trim() === '') {
                alert('Please enter a closure reason.');
                return;
            }

            // Pass back the reason and incident sys_id
            GlideModal.get().destroy({
                result: 'closed',
                reason: reason,
                incident_sys_id: '${jvar_incident_sys_id}'
            });
        }
    </script>
</j:jelly>

Explanation:

  • We have a textarea for the user to enter their reason.
  • The submitClosureReason function retrieves the value, performs a basic validation, and then uses GlideModal.get().destroy() to close the modal, passing back an object containing the status (‘closed’), the reason, and the incident sys_id.

Step 2: Create a UI Action to Trigger the Closure Reason Modal

Name: Close Incident with Reason

Table: Incident [incident]

Action name: close_incident_reason

Form button: Check this box.

Client: Check this box.

Onclick: openClosureReasonModal();

Script:

function openClosureReasonModal() {
        var incidentSysId = g_form.getUniqueValue();
        var incidentNumber = g_form.getValue('number');

        var gdw = new GlideModal({
            title: 'Closure Reason',
            contentURL: 'incident_closure_reason_modal.do?sysparm_id=' + incidentSysId + '&incident_number=' + encodeURIComponent(incidentNumber),
            onClose: function(result) {
                // Check if the modal was submitted and not just closed
                if (result && result.result === 'closed') {
                    var closureReason = result.reason;
                    var incidentID = result.incident_sys_id;

                    // Now, update the incident record
                    var gr = new GlideRecord('incident');
                    if (gr.get(incidentID)) {
                        gr.state = 7; // Assuming state 7 is 'Closed'
                        gr.close_notes = closureReason;
                        gr.update();
                        g_form.addInfoMessage('Incident ' + gr.number + ' has been closed with the reason: ' + closureReason);
                        // Reload the form to reflect changes
                        g_form.reloadPage();
                    } else {
                        g_form.addErrorMessage('Failed to retrieve incident record for closure.');
                    }
                } else {
                    g_form.addInfoMessage('Incident closure cancelled.');
                }
            }
        });
        gdw.render();
    }

Explanation:

  • The UI Action opens the incident_closure_reason_modal.do UI Page, passing the incident’s sys_id and number.
  • The onClose function is where the magic happens:
    • It checks if result exists and if result.result is ‘closed’. This distinguishes between a user closing the modal without submitting and a successful submission.
    • It extracts the reason and incident_sys_id from the returned object.
    • It creates a GlideRecord for the ‘incident’ table.
    • It uses gr.get(incidentID) to fetch the specific incident record.
    • It sets the state to ‘Closed’ (assuming ‘7’ is the correct sys_id for your Closed state) and populates the close_notes field with the provided reason.
    • gr.update() saves the changes to the database.
    • An informational message is displayed, and g_form.reloadPage() refreshes the form to show the updated state and fields.

This example showcases how to collect data from a modal, process it, and update the underlying ServiceNow record, making GlideModal a powerful tool for custom business processes.

Advanced Considerations and Best Practices

  • Error Handling: Always include robust error handling in both your UI Page and the calling script. What happens if the UI Page fails to load? What if the data retrieval or update fails?
  • Security: Ensure your UI Pages and the data they access are secured appropriately using ACLs and ServiceNow’s security framework. Avoid passing sensitive information directly in URL parameters if possible.
  • Performance: For complex UI Pages, consider their loading time. Large amounts of client-side JavaScript or inefficient Jelly can impact performance.
  • Responsiveness: Design your UI Pages to be responsive, especially if they will be accessed on different devices.
  • GlideModalAPI vs. GlideDialogWindow: While GlideDialogWindow was the older API, GlideModal is the modern, preferred API. Use GlideModal for new development. It offers better styling and a more consistent user experience.
  • Using `g:set` for Variables: Within Jelly, use <g:set var="myVar" value="${someValue}" /> to declare variables that can be accessed within the Jelly script.
  • Passing Complex Data: For passing more complex data structures, consider stringifying JSON objects and passing them as URL parameters, then parsing them back in the UI Page.

Troubleshooting Common GlideModal Issues

Even with careful planning, you might encounter issues. Here are some common problems and how to address them:

1. Modal Doesn’t Appear or Shows an Error

  • Check the Console: Open your browser’s developer console (usually by pressing F12). Look for JavaScript errors. Errors in your client script or the UI Page’s script will be reported here.
  • Verify contentURL: Double-check that the UI Page name in the contentURL is spelled correctly and exists. Ensure there are no typos in the URL parameters.
  • Client-Side Scripting: Make sure the UI Action is marked as “Client” and has a function assigned to “Onclick”.
  • UI Page Rendering: In your UI Page, ensure the Jelly is well-formed and that the embedded JavaScript is syntactically correct.
  • ACLs: Verify that the user has read access to the UI Page itself.

2. Data Not Being Passed Correctly to the UI Page

  • URL Encoding: Always use encodeURIComponent() for any variable data you pass in the URL to prevent issues with special characters.
  • Parameter Names: Ensure the parameter names used in the contentURL exactly match the names you are trying to retrieve using RP.getWindowProperties().get('parameter_name') in the UI Page.
  • `sysparm_` Prefix: Standard ServiceNow URL parameters often start with sysparm_. While you can use other names, be aware of common conventions.

3. Data Not Being Returned Correctly from the UI Page

  • `onClose` Callback: Ensure your onClose function is correctly defined in the GlideModal call.
  • `GlideModal.get().destroy()`: Verify that you are calling GlideModal.get().destroy() within your UI Page’s script and that you are passing the correct object/value for the `result` parameter.
  • Object vs. Primitive: Remember that result in the onClose callback can be a primitive value (like a string or boolean) or an object, depending on what you pass to destroy(). Check the type in your callback.

4. Styling Issues in the Modal

  • Global CSS vs. Local CSS: UI Pages inherit global styles, but you can override them with your own CSS within the UI Page’s HTML or by linking to a UI Script. Be mindful of Bootstrap classes, as ServiceNow relies heavily on it.
  • Scoped CSS: If you’re using scoped applications, ensure your CSS is correctly scoped.

Interview Relevance: Why GlideModal is Important

For any ServiceNow interview, especially for development or advanced administration roles, understanding GlideModal is a significant advantage. Interviewers often probe for practical application and problem-solving skills. Here’s why it’s frequently asked about:

  • Core Skill Demonstration: It shows you can go beyond basic form customization and build more interactive user experiences.
  • Understanding Client-Side Scripting: Effectively using GlideModal requires a solid grasp of client-side JavaScript, Jelly, and ServiceNow’s client-side APIs.
  • Problem Solving: Questions might involve scenarios like: “How would you create a popup to collect additional information before closing a change request?” or “Describe a situation where you used a modal to improve user workflow.”
  • API Knowledge: Being able to discuss GlideModal.open(), GlideModal.get().destroy(), and how to pass parameters is essential.
  • UI Page Design: It demonstrates an understanding of how to build modular UI components.
  • Best Practices: Discussing performance, security, and error handling related to GlideModal shows a mature approach to development.

Example Interview Question: “Can you explain how you would use GlideModal to display a confirmation dialog before a user submits a critical record update, and how you would retrieve the user’s confirmation status?”

Answer Strategy: Mention creating a simple UI Page with “Yes” and “No” buttons. Explain how to call it using GlideModal.open(), passing a `contentURL` to the UI Page. Crucially, describe the `onClose` callback function in the UI Action and how it receives the ‘Yes’ or ‘No’ status passed back from the UI Page’s `GlideModal.get().destroy()` method, and then conditionally proceed with the record update.

Conclusion

GlideModal is an indispensable tool in the ServiceNow developer’s arsenal. By mastering the art of calling UI Pages with GlideModal, you can significantly enhance user experience, streamline complex processes, and build more dynamic and responsive applications. Whether you’re developing custom solutions, automating workflows, or simply looking to impress in your next interview, a deep understanding of GlideModal will undoubtedly set you apart.

Remember to practice, experiment with different scenarios, and always refer to the official ServiceNow documentation for the most up-to-date information and advanced features. Happy coding!


Scroll to Top