A Hidden Way to Show Form Messages in ServiceNow

I was recently doing some research on g_form methods that ServiceNow provides to display messages on the form. The main two that I was familiar with were “addInfoMessage” and “addErrorMessage”; but what if there were more? I have definitely seen other messages displayed that was neither of these two… After doing some digging for the “hidden” method I was rewarded. The new method I found was called “_addFormMessage”. Note that this is not documented and is prefixed with an underscore indicating that it was meant to be used as an internal ServiceNow method so it is subject to change although it has been there since at least Eureka if that tells you anything :). Enjoy!

g_form._addFormMessage('Testing a form message at the top');

Ok, so that’s fine and dandy, but what if at some point in the future ServiceNow did deprecate this command; here’s one way you could re-implement it yourself using jQuery. Note that in order to make the command accessible via “g_form” we need to create it as a function off of the GlideForm object:

String.prototype.capitalizeFirstLetter = function() {
	return this.charAt(0).toUpperCase() + this.slice(1);
};

GlideForm.prototype.setNewFormMessage = function (message) {
	var tableId = g_form.getTableName().toString().capitalizeFirstLetter();

	var htmlBlock = '<span class="section " id="section-test" data-header-only="false"><div id="' + tableId + '.section_header_spacer"></div><div class="outputmsg_container" style="display: block;" id="output_messages"><button class="btn btn-icon close icon-cross" onclick="GlideUI.get().clearOutputMessages(this); return false;"><span class="sr-only">Close Messages"</span></button><div class="outputmsg_div"><div class="outputmsg outputmsg_ notification notification-"><img class="outputmsg_image" src="images/outputmsg__24.gifx" alt=""><span class="outputmsg_text">' + message + '</span></div></div></div>';

	jQuery("[tab_caption=" + tableId + "]").after(htmlBlock);
};


var message = 'testing 123';
g_form.setNewFormMessage(message);

How to Send Server Data to the Client using Jelly in ServiceNow

One thing that I’ve seen more than a few people struggle with is how to get data from the Server to the client when using Jelly in UI Pages and UI Macros. As such I wanted to give some examples today which would help. The “secret” is that you can use the same “Server-side” (Jelly) variables inside the Client script section (on UI pages) or within “script” tags on UI Macros.

So as in our examples below, let’s say that you had a Jelly variable called “serverArr” which you declare within the “g:evaluate” tags which gets processed on the Server-side. The evaluate code loops through a table and builds a collection of JSON objects which get added to this array variable. To use this object we need to first encode it into JSON on the server, then we can simply use the same variable name within the corresponding client script section of “${serverArr}” (without double quotes), and when ServiceNow goes to build the Page/Macro it will substitute the variable in the client script with our data from the server.

UI Page

<!--?xml version="1.0" encoding="utf-8" ?-->

    
        
        var serverArr = [];
        var gr = new GlideRecord('incident');
        gr.setLimit(5);
        gr.query();
        while (gr.next()) {
        var obj = {}
        obj.number = gr.number.toString()
        obj.short_desc = gr.short_description.toString();
        serverArr.push(obj);
        }

        serverArr = new global.JSON().encode(serverArr);
        serverArr; 
       

Working with the following “Server data” on the Client side:

Client Script Section of UI Page: Note that this must be pasted into this section via the list view of the page otherwise it will complain about the use of the ${serverArr} variable.

   var objArr = ${serverArr};
   jQuery('body').append(JSON.stringify(objArr) + '');
   for (var i= 0; i < objArr.length; i++) {
        var obj = objArr[i];
        jQuery('body').append(obj.number + ' - ' + obj.short_desc + '');
   }

Server Data - UI Page 1

UI Macro

<!--?xml version="1.0" encoding="utf-8" ?-->
    var serverArr = [];
    var gr = new GlideRecord('incident');
    gr.setLimit(5);
    gr.query();
    while (gr.next()) {
    var obj = {}
    obj.number = gr.number.toString()
    obj.short_desc = gr.short_description.toString();
    serverArr.push(obj);
    }

    serverArr = new global.JSON().encode(serverArr);
    serverArr;
 
/<script> /
var objArr = ${serverArr};
   jQuery('body').append(JSON.stringify(objArr) + '');
   for (var i= 0; i < objArr.length; i++) {
        var obj = objArr[i];
        jQuery('body').append(obj.number + ' - ' + obj.short_desc + '');
   }
/</script>/

Note that the above script sections do not have a “/” I just needed to do that as WordPress kept trying to render them as in-line scripts!
Server Data - UI Macro

How to Make Variables Read Only Using g_form in ServiceNow

Variables Read Only

I was recently working with a colleague who mentioned that he had seen a new (un-documented) g_form method which allows you to set all catalog item Variables to Read Only. As you can probably imagine, this is great news for all ServiceNow developers who in the past had to resort to DOM manipulation, custom Business Rules or lots of UI Policies (thanks to my mentor Mark Stanger for providing information on these options in the past) just perform this trick (makes it so fulfillers of tasks update variables but they can’t be updated from the catalog item request). Without further waiting; here’s the magic:

g_form.setVariablesReadOnly(true);

Easy Trick to Workaround ServiceNow’s Script Validation Checker

Sometimes you may want to load a script that is functionally correct but ServiceNow has an issue with it. One example of this would be if you are trying to load a 3rd party JavaScript library but there is some code in it that ServiceNow throws an error on that you know is correct. In these circumstances you can add the Script field to the List View. Then Paste in your script.
Load Client Script via List View

Remove the Default “time” for DateTime fields in ServiceNow

Removed Default Time in DateTime Fields

As a ServiceNow Developer/Consultant one enhancement that I have received a number of times over the years (usually for the Change Management form) is the ability to “blank” the default timestamp (set to the current time) that is automatically pre-filled on all date-time fields. The customer wants to force the user to specify their own time and are worried that the user will simply choose a date and then click the checkbox to exit the control leaving in the current time. To mitigate this I have created a script that utilizes the DOM (Yeah, I know what you’re thinking but I have found no other way, and besides, ServiceNow uses the DOM via Prototype to create the control). The main thing to worry about is that this “could break after upgrades”, so you will want to add this to your list upgrade test cases as this has broken from Fuji to Geneva (see comments to find out the two lines that changed). If anyone has found a better way to do this which does not include modifying the DOM please let me know!

Ok, so on to the fun. Initially we define the list of fields we want to “blank” (must include the table as well) the time for, then we run a jQuery statement to select each element on the form which is of type “glide_element_date_time” (updated in Geneva, was “date_time” previously). From there we pull the data-ref attribute to match against our list of fields to set and add an event to call our controlDateTimeSelector function (with a timeout) when the user clicks the corresponding calendar button/icon. We will try a maximum of 5 times (500ms between each) to get the popup DateTime control before we give up. In my testing I have never had it reach the 2nd iteration but it is technically possible if the system is slow.

Enough talk, enjoy the code!

function onLoad() {
   var fields = ['change_request.start_date','change_request.end_date','change_request.work_start', 'change_request.work_end'];
   try {
      //jQuery('a[data-type="date_time"]') this worked in Fuji...
      jQuery('input[data-type="glide_element_date_time"]')
         .each(function (index, value) {
            var ref = jQuery(this).attr('data-ref');
            if (ref.length) {
               jslog('JM: found element at index: ' + index + ' value: ' + ref);
               var val = g_form.getValue(ref);
               if (val == '' && fields.indexOf(ref) > -1) {
                  jslog('JM: registering event for element at index: ' + index + ' value: ' + ref);
                  //need to call .next() in Geneva/Helsinki, not needed in prior versions, to get the calendar button beside the ref field
                  jQuery(this).next().on('click', function () {
                     setTimeout(controlDateTimeSelector, 500);
                  });
               }
            }
         });

   } catch (e){
      console.error('JM: Error occurred: ' + e.toString() + ' on line:' + e.lineNumber);
   }

   function controlDateTimeSelector(iterator) {
      jslog('JM: Starting controlDateTimeSelector.');
      var prefixId = '#GwtDateTimePicker';
      try {
         if (iterator === undefined) {
            iterator = 0;

         } else if (iterator > 5) {
            jslog('JM: Stopping; hit max iteration count.');
            return
         }

         if (!jQuery(prefixId + '_header').length) {
            jslog("JM: Didn't find DatePicker, trying again in 500ms. Iterator was: " + iterator);
            iterator++;
            setTimeout(controlDateTimeSelector.bind(null, iterator), 500);

         } else {
            jslog('JM: Found and Set DatePicker.');
            jQuery(prefixId + '_hh').val('--');
            jQuery(prefixId + '_mm').val('--');
            jQuery(prefixId + '_ss').val('--');
         }
      } catch (e){
            console.error('JM: Error occurred: ' + e.toString() + ' on line:' + e.lineNumber);
      }
   }
}
}

On-The-Fly Event Creation in ServiceNow

Recently I was doing some troubleshooting for an onChange script and trying to figure out why it (or rather it’s event) wasn’t firing. In my digging I came across the code that is responsible for registering events and then kicks off the corresponding scripts. I can’t think of a lot of great scenarios off the top of my head, but perhaps when one field changes, you want the system to then start monitoring for 2 other fields to change (maybe user has to do something in a sequence?); just throwing ideas against the wall but you get the idea; keep it in your virtual toolbox ;).

First we throw our script into a function (should look familiar) only difference to the usual is that we are giving it a name (instead of just “onChange”). In this case we’re naming it “onChange_incident_script” but the name can be whatever you want. Next, we will instantiate a GlideEventHandler object. This object will raise our event and call the script when it fires, it expects 3 parameters: The name of the event handler, the script to run, and the field (in the format of table.field) to monitor:

function onChange_incident_script(control, oldValue, newValue, isLoading, isTemplate) {
    if (isLoading || newValue == '') {
        return;
    }
    jslog('JM: Running Script from on-the-fly event. Original Value was: ' + oldValue);
    //Your code here...
}
var thisHandler = new GlideEventHandler('onChange_incident_impact_change', onChange_incident_script, 'incident.impact');
g_event_handlers.push(thisHandler);

With that said, some of you may be thinking, well why can’t we just use jQuery and do the same thing? Yes, for the most part, however you wouldn’t get the nifty “control, oldValue, newValue, isLoading, and isTemplate” variable data to play with. Just for those curious minds out there, here’s how we could do the close to the same thing in jQuery (minus the extra SN objects):

//we need to use double backslashes to escape the periods in the selector
jQuery('#element\\.incident\\.impact').on('change', function() {
    jslog('JM: Running Script from on-the-fly event. Original Value was: ' + oldValue);
    //Your code here...
});

How to Get the DisplayValue of a Reference on the “Client Side” in ServiceNow

I was working with a client recently and had the need to get the Display Value of a Reference field that was on the form. I didn’t want to have to make an AJAX call just to pull this value (on the server side you would use the “getDisplayValue()” function), and that’s when it hit me. The client already has this value, why not use it? Initially I was thinking about DOM/jQuery which was not going to be ideal. As I dug further I uncovered a little known method to pull the Display Value from a reference field. Drum roll please!

g_form.getDisplayBox('field_name').value

Hopefully this saves someone some time in the future.

How to send REST data using the “PATCH” Method from ServiceNow

REST Example

 

When beginning an integration it is important to identify which capabilities are supported on both endpoints. In my case I was trying to integrate two systems which supported REST but not all HTTP Methods were supported by ServiceNow. ServiceNow supports the following Outbound REST Methods:

GET – Pull records

POST – Create records

PUT – Update records

DELETE – Delete records

Unfortunately for me in this case the remote system’s provided API only supported GET/POST/PATCH as a means to pull/create/update (PATCH usually used to partially update) records on the remote system and unfortunately sending REST data using the “PATCH” method is not currently supported in ServiceNow*. So what if you want to integrate with a 3rd party API that only works with PATCH to update data?

The answer, hope that the 3rd party provides an override method (most Microsoft products) that can be used with “POST” or “PUT”, both of which ARE supported in Outbound REST ServiceNow. In this scenario the 3rd party application has written special logic to handle incoming REST messages that are using one method (POST/PUT), but are checking for Request Headers to determine if the incoming data should be handled in another fashion (in our case PATCH).

The key to making this work (apart from confirming the 3rd party supports this) is in the HTTP Header of your REST Message you will need to set the Request Headers either in your REST Message or via code (depending on how you are implementing your REST functionality). Then setup the method to use POST or PUT so that ServiceNow knows how to send the REST message.

Below is an example of both ways in which we use the POST method to override in these examples; in this example the Request Headers needed were for ContentType and Override:

  • X-HTTP-Method-Override = “PATCH”
  • Content-Type  = “application/json-patch+json”

Here’s our example setting it up via a REST Message definition:

REST PATCH Method3

Or setting it up entirely in code:

var r = new sn_ws.RESTMessageV2();
r.setRequestHeader("Accept","application/json");
r.setRequestHeader('Content-Type','application/json-patch+json');
r.setRequestHeader('X-HTTP-Method-Override','PATCH');
r.setHttpMethod('post');
var url = 'https://integration.url.com';
r.setEndpoint(url);

*Note that PATCH is supported inbound to ServiceNow using REST, but does not support outbound PATCH (even though the REST API Explorer tool will generate non-working outbound PATCH code for you…) as of this post (in Helsinki).