SN Pro Tips

View Original

Requiring Attachments (& Other Miracles) in Service Portal

Note: Want to download an update set containing a packaged version of this functionality? Skip to the bottom! But don't forget to at least check out the catalog client script for a usage example.

Unless you're somehow still rocking UI15 and do most of your development on a Commodore 64, there's a good chance that by now you're at least vaguely familiar with ServiceNow's new(-ish) Angularized end-user front-end feature: the Service Portal.

If you've tried to implement your Service catalog in the Service Portal, there's also a good chance that you've wept openly over your keyboard, trying to find an effective and non-hackey way to replicate functionality that was readily available using out-of-box APIs in the old Jelly-based CMS and catalog form. Stuff you'd think would be really basic, especially two full major releases later, it just not possible in the Service Portal. g_form APIs like getControl() and getElement() (and gel()) don't exist in Service Portal. Neither does any synchronous GlideRecord query or GlideAjax call, meaning that if you want to check something on the server in an onSubmit() script, you're out of luck. 

One of the biggest annoyances resulting from this, is that there is no good way to check if a catalog item has attachments as the user submits the form. This means that you cannot make attachments required on a specific catalog item.
Oh sure, there are sort of ways to do it, such as modifying the catalog item widget just for that catalog item, so that the client controller and server script work together to check for attachments. Or you could embed the whole CMS or ITIL UI catalog item form in an iframe. Unfortunately, both of those have major downsides. 

I've made this solution, in order to restore some basic functionality, with a mind toward the specific problem of checking for attachments in the Service Portal. 

Here is a basic feature-list of the below functionality: 

  1. Require attachments on submission, using sp_form.getAttachments()

    • Require attachments of a specific type

    • Require a specific number of attachments

    • Require a specific number of attachments of various types

  2. Access DOM element of variable field input box and div element, using sp_form.getElement() and sp_form.getControl()

  3. Access variable names, sys_ids, and question text, using sp_form.variables or sp_form.getVariables().

  4. Iterate over each variable on the page, or otherwise retrieve variable sys_IDs, names, and question text without having prior knowledge of each within your catalog client script.

In order to enable some of the functionality we're used to, we have to make use of a method which you may be familiar with from my previous article on enabling DOM access in the service portal; but first, let's create a Script Include that we can make use of to enable a query we're going to need to run later. 

Script Include

Name: CatItemVariables
Client Callable: True
Script: See below:

See this content in the original post

In this client-callable Script Include, we accept two arguments: a catalog item, and (optionally) a field name to retrieve for each one. We then create an array, and populate it with that field from each catalog item variable associated with the indicated catalog item. We're going to use this to build a query string later on. 

Next, we need to build a UI Script that's going to be included as part of a JS Include, which we'll associate with the Theme that's included on the Portal on which our Service Catalog will be used. This UI Script will primarily be responsible for constructing the sp_form object, and its' various API methods. Here's how to construct that UI Script: 

UI Script

Name: sp_form
Global: False
Description: I recommend writing a reasonable description of this script, as with all useful scripts, so future developers will know what it's for
Script: See below:

See this content in the original post

This UI Script is a little complicated, but here's a basic run-down of what it's doing: 
First, we construct an object called sp_form. We give it a method called getAttachments(), and a private constructor method for building custom CatalogAttachment objects, which we use in the getAttachments() method. This constructor just builds an object with a known set of properties associated with Catalog Attachments, so we don't have to keep declaring new generic objects for each one. 

After initializing the sp_form object, we then want to build on it to include a list of variables and the methods for accessing them as well as for accessing elements based on variable names. This is accomplished by the getVariables() function. Since DOM elements in the service portal are given IDs and names based on the variable sys_IDs rather than names, we need to get the sys_ID for each variable as well. That is also handled by the getVariables()

The getVariables() function in this script is responsible for making a REST call using vanilla JavaScript (since GlideAjax isn't available to JS Includes). This REST call will retrieve the list of variables associated with the specified Catalog Item, using a query that utilizes the Script Include we defined first in this article. 

Once we've created the necessary UI Script, we'll need to create a JS Include as a wrapper, which we'll then associate with the Theme for our Service Portal. Follow the steps below, to set this up. 

JS Include

  1. Navigate to the sp_js_include table, by entering "sp_js_include.list" into the Application Navigator's filter bar, and pressing Enter. Click New to create a new JS Include record.

    1. Enter sp_form in the Display name field.

    2. Make sure Source is set to UI Script, then select the sp_form UI Script by entering it into the Source field.

    3. Save the JS Include record.

  2. Next, associate the JS Include with a given Theme.

    1. Navigate to Service Portal > Portals (the sp_portal table).

    2. Select the Service Portal you'd like to make your Service Catalog available in.

    3. On the record you select, you'll see a Theme field. Click on the reference icon to navigate to this Theme record.

      • Note that you may need to click the reference icon, then click Open record on the sys_popup view.

    4. Once on the Theme record, scroll down and select the JS Includes related list. If you don't see this list, add it.

    5. Click on the Edit button, and move the sp_form JS Include from the left side, to the right; then click Save.


That's it! Now your JS Include (And thus, your sp_form UI Script) will load on every page on that Service Portal. This JS Include is relatively small, will be optimized by ServiceNow, and uses an asynchronous REST request, so the performance impact will be negligible. 

Now, all that's left is to create a Catalog Client Script that makes use of this new sp_form API. Of course, this will vary greatly based on what you want to accomplish with the UI Script, but I'll provide an example below. Note that for the Catalog Client Script to run on the Service Portal, the UI Type field must be either Mobile / Service Portal, or All. If you select All, your script should work on both the Service Portal, and CMS view. Desktop Catalog Client Scripts will not run on the Service Portal. This is a bit of a misnomer, because of course the Service Portal can run on "desktops" as well.

Catalog Client Script

Name: Require CSV Attachment
UI Type: All
Type: onSubmit
Applies on Catalog Item view: True
Script: See below:

See this content in the original post

The first thing we're doing in this Catalog Client Script, is checking if we have access to document and gel(). If we have access to those, then we're not in the Service Portal, so we use the normal method of checking for attachments. 

If we do not locate those objects however, then we assume that we are in the Service Portal, and we check to make sure that sp_form and the getAttachments() API are both present (line 14). If not, we throw an error to the console but we don't raise one to the level that the user would see it. You can change this behavior by adding throw new Error(); to a new line below line 40. 

If our sp_form API is present, then we use it to get the list of attachments on the current catalog item. Since this example is looking for at least one CSV attachment, we then iterate over each attachment and check its' extension. If the extension is "CSV", then we break out of the loop and allow the form to be submitted. However, if we do not find any such attachment, or if the required number of attachments are not present (though in this case, only one is required), we return false; thus halting submission. We also display an error to the user, alerting them to the attachment requirement. 


Download Update Set

Don't feel like copy/pasting all that code? No problemo! Simply download the update set below, and follow these instructions to install it into your ServiceNow instance. 

Download latest version


See this content in the original post