Contents
In this blog, you will learn about Configurable Workspaces, how to use moment.js, load UI Scripts and perform data validation.
What is a Configurable Workspaces (WEP)
What is it?
ServiceNow has moved beyond the initial Agent Workspace to introduce Configurable Workspaces (WEP). With WEP, ServiceNow lets users shape their workspace to fit different roles, from HR to agents. It’s a step towards making things more tailored and up-to-date. Plus, users can easily switch between this new setup and the Classic Environment. Still confused? Ashley Snyder has a great rundown on the Community: Learn about the differences between Agent Workspace (Legacy) and new Configurable Workspaces that is well worth a read.
Why do I need to use a WEP?
ServiceNow is aggressively rolling out WEPs across its product suites. Most new functionality is built into these new workspaces, with some being unavailable in the classic environment. Take the Service Operations Workspace for ITSM as an example. Without it, you can’t use Agent Chat, integrated Microsoft Teams chat, Incident Investigation and many more. To get the most value from your ServiceNow investment, moving to WEPs is a must.
Service Operations Workspace
Most ServiceNow customers started on ITSM, so moving to Service Operations Workspace first might make a lot of sense. But don’t be too hasty – not all processes have been integrated at this moment. Problem management is still waiting to be ported, and Change Management has only just made the move across in Version 2.0.0 as part of the February 2023 store release. For new customers, or customers looking for an opportunity to return to out of the box, adoption is a must. If you are considering migrating across, be mindful that some of your scripts will not work. A common script to change that will fail is date validation, which is embedded into the Change process.
Validating Multiple Dates
Most customers reasonably expect date validation when scheduling a change. A modern, contemporary offering like ServiceNow would surely prevent users from doing silly things such as scheduling a change that ends before it begins, or starts in the past, or on your birthday (sarcasm detected!). There is some basic validation provided out of the box, but developers are often asked to add more nuance. If you are migrating from the classic environment with anything that has a lot of scripted date validation to WEP, chances are they will no longer work. The two core variables and functions commonly used, g_user_date_time_format
and getDateFromFormat()
, are no longer available. If you have imported the moment.js library, moment()
will also stop working too. Many of your UI Scripts will suffer the same fate.
WELP! My UI Scripts Don’t Work
UI Scripts loaded into core UI won’t necessarily work in a WEP. It isn’t just a matter of changing your Client Scripts UI Type to All; the API for loading in scripts is completely different:
function onLoad() { g_ui_scripts.getUIScript('moment.js').then(function(script) { var myThing = new script(); }); }
UI Scripts also need to return something. Check your console for errors like this:
Your UI Script needs to follow this pattern:
(function () { return "Hello world" });
Fixing moment.js for WEP
To get moment.js to run we need to wrap it in a function and return a moment object. Using the pattern above, I copied the code of the latest version of moment.js and returned it using the UI Script pattern mentioned above. Your UI Script is isolated from the Client Script that invoked it, so you need to pass it in instead of using this
.
Alternatively, you can just download the UI Script moment.js4WEP I created on ServiceNow Share.
Once you have installed it in your system, you can invoke it in an onLoad Client Script for the context required. Don’t forget to set the UI Type to Mobile/Service Portal.
function onLoad() { g_ui_scripts.getUIScript('moment.js.4WEP').then(function(momentJSInitialiser) { // Pass in 'this' to give all client scripts (and ui actions) access to moment() new momentJSInitialiser(this); console.log("g_date_time_format:" + g_date_time_format ); console.log(this); }); }
Now moment() API will be available to all of your client scripts running for the chosen table.
Start/End Date Example
Take this example Client Script written in the classic environment to validate start and end date.
function onSubmit() { var startDate = g_form.getValue("start_date"); var endDate = g_form.getValue("end_date"); var format = g_user_date_time_format; if (startDate === "" || endDate === "") return true; // get date strings into a number of milliseconds since 1970-01-01 var startDateMs = getDateFromFormat(startDate, format); var endDateMs = getDateFromFormat(endDate, format); if (startDateMs < endDateMs) return true; g_form.clearMessages(); if (startDateMs < endDateMs) return true; g_form.clearMessages(); if (startDateMs > endDateMs) { g_form.addErrorMessage("Planned state must be after Planned end"); return false; } }
We mentioned earlier that the g_user_date_time_format
variable is not defined in Client Scripts running in UI Type Mobile/Service Portal (or ‘All’ and executing in a WEP). This will store the date/time format the user is using, which allows us to convert the value of the DateTime field to unix time, which can be used for comparing dates.
For some bizarre reason, in WEP this data is stored in a variable called g_date_time_format
instead.
But when we output g_date_time_format
we get yyyy-MM-dd HH:mm:ss
, which differs slightly from what is shown to the user.
This is also the case with the old variable g_user_date_time_format
, but the getDateFromFormat()
function handled it.
Since getDateFromFormat(string, string)
is not defined, so we will use moment(string, string).valueOf()
it instead.
We need to use moment(datetime, format)
– but it expects the format shown to the user on the form with uppercase for the date format, and will not parse correctly using the value provided by ServiceNow. ššš.
As per the moment.js documentation, we will need to change the yyyy and dd to uppercase for it to be parsed.
The final code is below:
function onSubmit() { var startDate = g_form.getValue("start_date"); var endDate = g_form.getValue("end_date"); var format = g_date_time_format.replace("dd","DD").replace("yyyy","YYYY"); // moment.js expects upper case for date if (startDate === "" || endDate === "") return true; // get date strings into a number of milliseconds since 1970-01-01 var startDateMs = new moment(startDate, format).valueOf(); var endDateMs = new moment(endDate, format).valueOf(); if (startDateMs < endDateMs) return true; g_form.clearMessages(); if (startDateMs > endDateMs) { g_form.addErrorMessage("Planned state must be after Planned end"); return false; } }
Taking a Moment to Reflect
Well, that was a baffling ordeal! After a series of unexpected challenges, we have got DateTime validation working in a WEP. Once it is all set, and the nuances understood, it is very easy to use.
No Code/ Low Code?
At this stage you might be thinking… why didn’t I consider a low code approach such as UI policies? And that would be a great thought and was actually the first thing I tried. Fellow MPV Mark wrote a fantastic article No Code Date Validations through (Catalog) UI Policies that demonstrates just how it can be done. For some reason, this was not working for me at all! After wasting an hour or so scratching my head, questioning why I couldn’t get a simple UI policy to work, I decided to go back to trying to script it.
It turned out there is currently a defect where some global UI Policies set to run in all views don’t work in WAP (PRB1682768 : UI policies are not applied to the workspaces form). But I only found this out later!
That being said, data validation requirements are often nuanced, with UI policies also requiring scripting. May as well just script it so it is easier to find.
Nobody uses moment.js anymore!
The moment.js project is no longer active:
We now generally consider Moment to be a legacy project in maintenance mode. It is notĀ dead, but it is indeedĀ done.
momentjs.com
It is reasonable to expect in 2023 that there would be a native solution to handling date formats. There are some Date() functions that assist with local time zones, and the Intl
Ā library can be used to solve some format issues. I still wasn’t able to find anything that solves this as elegantly as moment() and will continue to use it. You can also use moment.js server-side.
3 comments
Great read! Thanks SNNERD!
Interesting read. We’ve been running into multiple problems with client scripts not working in workspaces compared to the classic environment.
I’d have loved to taken a look at how exactly you wrapped moment.js but for me the link seems to be broken.
Thanks for letting me know!
The Share project was private. It is now public so the link will work now.