Over the past year or so of working with Service Portal I’ve learnt a lot, and I can particularly appreciate the power it gives one to rapidly prototype and build modern and engaging cross-device user experiences.
Throughout this time one of the things I’ve learnt are ways that fundamental development best practices can be applied to Service Portal to make working with it a lot easier. If applied correctly, these best practices can really help by improving complex applications in the following ways:
- Easier to maintain – so code is more readable and everything is in a place it logically makes sense to be;
- More flexible – so if a business requirement changes it requires less effort to support;
- More reusable – meaning the same assets can be leveraged in multiple ways;
- Performant – so navigation, scrolling, and general operation is fast and smooth.
When I came up with the idea for this blog post I decided to start a list, and once I had enough tips I’d write a post covering them all. However, very quickly the list got quite overwhelming and I realised that to cover each in sufficient detail would result in a very lengthy blog post! So I decided to break this list up into multiple posts. In this part 1 I’ll be covering one of the tips, and the rest will be covered in subsequent posts.
As always, leave a comment below if you have any questions or feedback, and follow and reach out to me on Twitter using my handle @dylanlindgren – I always tweet when a new article is posted. Also, if you use an RSS reader like Feedly, subscribe to my feed to stay up to date!
Leverage Script Includes as an API
The first part of a widget that runs when it is to be displayed is the server script. Within the server script you have access to all the server-side ServiceNow features that a developer would be used to – GlideRecord for example.
The goal with the server script is place data relevant to the widget in the data
object, so that it can be used by the front-end. Once the server script has run, Service Portal will automatically pass this object to the client for it to be used in the client script and HTML template.
Another purpose of the server script is to handle input. For example, if a user clicks a button in the widget and it should update a record, the processing of this request can happen in the widget’s server script.
For more about how the server script works, see the ServiceNow documentation’s widget developer guide.
So it’s a server script’s responsibility to handle reading and writing data to the database. The easiest way to do this would be to simply perform the GlideRecord operation right there. However, before you do that there’s some things that first need consideration:
- Are there any other widgets in your application which will also need this exact (or similar) data?
- How much more code will this require to be put in your server script? Is it going to make the server script hard to read?
- Do you plan to make the same data available over a REST API?
Furthermore, if the code is in a server script, it can’t easily be tested at the moment.
The “separation of concerns” principle is a core concept for ensuring good application design. An explanation of this is given by Lucas Oman on Stack Overflow:
Concerns are the different aspects of software functionality. For instance, the “business logic” of software is a concern, and the interface through which a person uses this logic is another.
The separation of concerns is keeping the code for each of these concerns separate. Changing the interface should not require changing the business logic code, and vice versa.
If we think of what the “concerns” of a widget are, it’s really to allow users to do two things:
- Visualise data
- Execute operations on data
So widgets are part of the user interface.
If we use an example of a widget which shows a list of open cases, the actual process of obtaining and updating of the cases is business logic. So following the “separation of concerns” principle, we shouldn’t be putting the code which gets a list of open cases within the widget’s server script (as a widget is user interface). So where should we put it?
It is this exact scenario where script includes come into play.
A best practice I try to follow is to take data-related activities & business logic out of the server script, and put them into a Script Include instead. By doing this you can:
- Simplify the server script;
- Make the server script self-documenting;
- Make code reusable across multiple widgets, and anywhere else on the server (e.g. scripted REST API’s);
- Break up the code into logical functions;
- Have a single place to update the code if the data model changes;
- Ensure that code you write is testable, as Script Includes can be tested via the Automated Test Framework.
For example, say you were building an app used to manage car repairs at a mechanics workshop. The app shows a list of cars, and the mechanic can click on a car and mark it as repaired. In this scenario, you could build a script include like so:
var CarsAPI = Class.create(); CarsAPI.prototype = { initialize: function () {}, getCars: function (carID, color) { // GlideRecord query here return cars; } getCar: function (carID) { // GlideRecord query here return car; } markRepaired: function (carID) { // GlideRecord query here return true; } type: 'CarsAPI' };
Then, within the widget which shows a list of cars, your server script could be this simple!
(function() { var $ca = new CarsAPI(); if (input && input.action === 'mark-repaired') { $ca.markRepaired(input.sys_id); } data.cars = $ca.getCars(); })();
Thanks for reading my article. Again, if you have any questions or feedback, feel free to leave a comment below!