Intro
When dealing with server-side scripts, the ServiceNow Debugger makes debugging relatively easy (most of the time), as you can actually see into the call stack, and the contents of your server-side variables as you step through your code, line-by-line. When available, that tool is incredibly useful; but unfortunately, it does not work with client scripts.
So, how do you troubleshoot client-side scripts in ServiceNow? Well, since those scripts execute inside the user’s browser, you’re going to have to use some browser-magic to make that happen. The good news is, modern browsers already have an incredible debugger that’s at least as good as the server-side script debugger in ServiceNow, built right in!
The question then becomes: “How do I trigger the client-side debugger? I can’t easily put breakpoints in my code that runs client-side, especially if it runs on-load; right?”
In fact, you can! Better yet, you can put calls to the debugger directly in your code!
In this short article, we’re going to see exactly how to do that, using a not-very-smart Client Script that runs on the Incident form. Join me after the jump, for a walk-through!
TLDR
Put the statement debugger; in your client-side code, wherever you want the debugger to take over and simulate a breakpoint.
How-to
I usually find the answer by way of example to be most effective, so let’s look at an example where we might need to troubleshoot a Client Script.
Note: I’m going to use a regular ol’ Client Script here, but this simple method works with Catalog Client Scripts, UI Policy scripts, UI Scripts, and even client-side scripts in the Service Portal! Basically, anything that executes as-written, in the user’s browser.
Let’s say we work for a company whose management is all about the micro. They seem adamant that for some unknowable reason, they need to be added to the "watch list” on every single Incident that one of their direct reports is assigned to.
”Okay, sure” you say, as you die a little inside; but you go to build this functionality for them.
From a previous ‘enhancement’, you’ve already got the “Manager” field on the form, which is auto-populated whenever the assigned_to field changes. You reckon you can use that field.
You create a new Client Script on the Incident table, you have it run “onChange”, and trigger it by changes to the “Assigned to” field.
Then, you write a bit of code. This code does the following:
Gets the value of the “manager” field.
Gets the current value of the watch list, as an array (or a blank array if the watch list is empty).
If the manager field is blank, or the manager is already on the watch list, halt and do nothing.
Add the manager to the watch list array, then update the value of the watch list field.
All pretty straight-forward. Here’s what that Client Script looks like once you’re finished:
However, when we trigger this code, nothing seems to actually happen! What’s going wrong here?
We could add a bunch of log statements into our code and faff about with it until we figure out what the problem is, and then hopefully remember how to get our code back into the state we need it in and remove all of our debug log code… Or, we could add just one line of code, such as on line 8 in the below snippet:
function onChange(control, oldValue, newValue, isLoading, isTemplate) { if (isLoading || newValue === '') { return; } var assigneeMgrID = g_form.getValue('manager'); var watchList = g_form.getValue('watch_list'); debugger; //If watch list has anything in it, split it into an array; // otherwise, replace with a blank array. watchList = (watchList ? watchList.split(',') : []); //If manager is empty or already on watch list, halt and do nothing. if (!assigneeMgrID || watchList.indexOf(assigneeMgrID) >= 0) { return; } watchList.push(assigneeMgrID); //Add manager to watch list array g_form.setValue('watch_list', watchList.join(',')); //update watch list field }
All we did here, was add a line with the statement debugger; on line 8, but now if we press F12 to open the console and then re-trigger our code, we get something more like this (the Google Chrome debugger)…
As you can see, the debugger has halted execution on the line in our code that has the debugger; statement. We can see the call stack, and loads of other stuff.
This can all look pretty overwhelming at first, but the main thing to pay attention to, is the Scope section. By default (in Chrome), this will be just below the Call Stack section on the right side of the debugger. Collapse Call Stack, and you’ll see Scope. In the Scope section, Local is usually going to be the most relevant bit. that’ll show you the values of the variables in your code at the moment the code halted.
As you can see in the image to the right, the variables in the local function scope in my code look as you’d expect, except for assigneeMgrID. Since the code paused after that variable was assigned, we would expect that variable to have a value!
I can use the buttons at the top-right of the debugger to step line-by-line through my code, or even into or out of a given function-call, or I can use step over to remain at the current level of the call stack (meaning, keep the debugger on my code).
By advancing step-by-step through my code, I can see that it’s evaluating my if block to be true, because assigneeMgrID is blank. This is causing the script to return early, and not do what I expected it to do.
Now that the debugger has helped us figure this out, we might go and investigate why this is. In this case, the issue is being caused by the fact that the Manager field is a dot-walked field. This means that my g_form.getValue(‘manager’) line would not work.
I could change that line to g_form.getValue(‘assigned_to.manager’), but that wouldn’t work either because the client script triggers when the Assigned to field is changed, not when the dot-walked Manager field is changed. (You can’t trigger a client script on change of a dot-walked/derived field). The manager field is updated asynchronously when the Assigned to field is updated, so when our logic runs, the assigned_to.manager field has not yet been populated.
Pro-tip: If you need to debug a callback function, adding debugger; will halt execution, but your current state may be lost, and the code won’t be visible. You can work around this though, by simply adding a second debugger; line in the code that runs synchronously, and just pressing the ‘continue’ button in the console debugger UI when it’s hit. That will allow your callback function to be debugged effectively!
In order to get this code to work, we’ll need to do an asynchronous query of some sort to get the user’s manager. This highlights another of the benefits to the debugger; statement: You can even put it in your callback function, to debug code in higher-order functions!
That’s all for this pro-tip. Thanks for reading!
If you have any questions, leave them in the comments below.
If you haven’t yet subscribed… are you okay? - Get subscribed below!
- March 2024
-
February 2024
- Feb 12, 2024 5 Lessons About Programming From Richard Feynman
- July 2023
- May 2023
- April 2023
-
December 2022
- Dec 13, 2022 ServiceNow Developers: BE THE GUIDE!
- October 2022
-
August 2022
- Aug 23, 2022 Using .addJoinQuery() & How to Query Records with Attachments in ServiceNow
- Aug 18, 2022 Free, Simple URL Shortener for ServiceNow Nerds (snc.guru)
- Aug 16, 2022 How to Get and Parse ServiceNow Journal Entries as Strings/HTML
- Aug 14, 2022 New tool: Get Latest Version of ServiceNow Docs Page
- March 2022
- February 2022
- May 2021
- April 2021
- February 2021
-
November 2020
- Nov 17, 2020 SN Guys is now part of Jahnel Group!
- September 2020
- July 2020
-
January 2020
- Jan 20, 2020 Getting Help from the ServiceNow Community
- December 2019
- November 2019
-
April 2019
- Apr 21, 2019 Understanding Attachments in ServiceNow
- Apr 10, 2019 Using Custom Search Engines in Chrome to Quickly Navigate ServiceNow
- Apr 4, 2019 Set Catalog Variables from URL Params (Free tool)
- Apr 1, 2019 Outlook for Android Breaks Email Approvals (+Solution)
- March 2019
-
February 2019
- Feb 27, 2019 Making Update Sets Smarter - Free Tool
-
November 2018
- Nov 29, 2018 How to Learn ServiceNow
- Nov 6, 2018 ServiceNow & ITSM as a Career?
- October 2018
- September 2018
-
July 2018
- Jul 23, 2018 Admin Duty Separation with a Single Account
-
June 2018
- Jun 19, 2018 Improving Performance on Older Instances with Table Rotation
- Jun 4, 2018 New Free Tool: Login Link Generator
-
May 2018
- May 29, 2018 Learning ServiceNow: Second Edition!
- April 2018
- March 2018
-
February 2018
- Feb 11, 2018 We have a new book!
- November 2017
-
September 2017
- Sep 12, 2017 Handling TimeZones in ServiceNow (TimeZoneUtil)
- July 2017
-
June 2017
- Jun 25, 2017 What's New in ServiceNow: Jakarta (Pt. 1)
- Jun 4, 2017 Powerful Scripted Text Search in ServiceNow
- May 2017
- April 2017
-
March 2017
- Mar 12, 2017 reCAPTCHA in ServiceNow CMS/Service Portal
-
December 2016
- Dec 20, 2016 Pro Tip: Use updateMultiple() for Maximum Efficiency!
- Dec 2, 2016 We're Writing a Book!
-
November 2016
- Nov 10, 2016 Chrome Extension: Load in ServiceNow Frame
- September 2016
-
July 2016
- Jul 17, 2016 Granting Temporary Roles/Groups in ServiceNow
- Jul 15, 2016 Scripted REST APIs & Retrieving RITM Variables via SRAPI
-
May 2016
- May 17, 2016 What's New in Helsinki?
-
April 2016
- Apr 27, 2016 Customizing UI16 Through CSS and System Properties
- Apr 5, 2016 ServiceNow Versions: Express Vs. Enterprise
-
March 2016
- Mar 28, 2016 Update Set Collision Avoidance Tool: V2
- Mar 18, 2016 ServiceNow: What's New in Geneva & UI16 (Pt. 2)
-
February 2016
- Feb 22, 2016 Reference Field Auto-Complete Attributes
- Feb 6, 2016 GlideRecord & GlideAjax: Client-Side Vs. Server-Side
- Feb 1, 2016 Make Your Log Entries Easier to Find
-
January 2016
- Jan 29, 2016 A Better, One-Click Approval
- Jan 25, 2016 Quickly Move Changes Between Update Sets
- Jan 20, 2016 Customize the Reference Icon Pop-up
- Jan 7, 2016 ServiceNow: Geneva & UI16 - What's new
- Jan 4, 2016 Detect/Prevent Update Set Conflicts Before They Happen
-
December 2015
- Dec 28, 2015 SN101: Boolean logic and ServiceNow's Condition Builder
- Dec 17, 2015 Locate any record in any table, by sys_id in ServiceNow
- Dec 16, 2015 Detecting Duplicate Records with GlideAggregate
- Dec 11, 2015 Array.indexOf() not working in ServiceNow - Solution!
- Dec 2, 2015 Understanding Dynamic Filters & Checking a Record Against a Filter Using GlideFilter
- October 2015
-
August 2015
- Aug 27, 2015 Easily Clone One User's Access to Another User